added ansible script
This commit is contained in:
@@ -9,16 +9,16 @@ postpone = Drafts
|
|||||||
folders-sort = INBOX,Draftr,Sent,Archive,Spam,Trash
|
folders-sort = INBOX,Draftr,Sent,Archive,Spam,Trash
|
||||||
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
|
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
|
||||||
|
|
||||||
[phil_notmuch]
|
# [phil_notmuch]
|
||||||
source = notmuch://~/Mail/phil
|
# source = notmuch://~/Mail/phil
|
||||||
query-map = ~/.config/aerc/notmuch-queries.conf
|
# query-map = ~/.config/aerc/notmuch-queries.conf
|
||||||
# outgoing = smtp://phil%40liphlink.xyz@mail.liphlink.xyz:587
|
# # outgoing = smtp://phil%40liphlink.xyz@mail.liphlink.xyz:587
|
||||||
# outgoing-cred-cmd = pass show mail/phil@liphlink.xyz
|
# # outgoing-cred-cmd = pass show mail/phil@liphlink.xyz
|
||||||
from = Phil <phil@liphlink.xyz>
|
# from = Phil <phil@liphlink.xyz>
|
||||||
default = Inbox
|
# default = Inbox
|
||||||
# copy-to = Sent
|
# # copy-to = Sent
|
||||||
postpone = Drafts
|
# postpone = Drafts
|
||||||
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
|
# aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
|
||||||
|
|
||||||
[spam_live]
|
[spam_live]
|
||||||
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
|
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
|
||||||
@@ -31,15 +31,15 @@ postpone = Drafts
|
|||||||
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
|
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
|
||||||
aliases = blue@liphlink.xyz,red@liphlink.xyz
|
aliases = blue@liphlink.xyz,red@liphlink.xyz
|
||||||
|
|
||||||
[spam_notmuch]
|
# [spam_notmuch]
|
||||||
source = notmuch://~/Mail/spam
|
# source = notmuch://~/Mail/spam
|
||||||
query-map = ~/.config/aerc/notmuch-queries.conf
|
# query-map = ~/.config/aerc/notmuch-queries.conf
|
||||||
outgoing = smtp://spam%40liphlink.xyz@mail.liphlink.xyz:587
|
# outgoing = smtp://spam%40liphlink.xyz@mail.liphlink.xyz:587
|
||||||
outgoing-cred-cmd = pass show mail/spam@liphlink.xyz
|
# outgoing-cred-cmd = pass show mail/spam@liphlink.xyz
|
||||||
from = Liph <spam@liphlink.xyz>
|
# from = Liph <spam@liphlink.xyz>
|
||||||
copy-to = Sent
|
# copy-to = Sent
|
||||||
default = Inbox
|
# default = Inbox
|
||||||
aliases = blue@liphlink.xyz,red@liphlink.xyz
|
# aliases = blue@liphlink.xyz,red@liphlink.xyz
|
||||||
|
|
||||||
# [proton]
|
# [proton]
|
||||||
# source = imap+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1144
|
# source = imap+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1144
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ index-format=notmuch://~/.local/share/mail
|
|||||||
#index-columns=flags:4,name<20%,subject,date>=
|
#index-columns=flags:4,name<20%,subject,date>=
|
||||||
border-char-vertical="│"
|
border-char-vertical="│"
|
||||||
border-char-horizontal="─"
|
border-char-horizontal="─"
|
||||||
# styleset-name=rose-pine
|
styleset-name=rose-pine
|
||||||
|
|
||||||
#
|
#
|
||||||
# Each name in index-columns must have a corresponding column-$name setting.
|
# Each name in index-columns must have a corresponding column-$name setting.
|
||||||
|
|||||||
42
ansible/.gitignore
vendored
Normal file
42
ansible/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Ansible
|
||||||
|
*.retry
|
||||||
|
.vault_pass*
|
||||||
|
/tmp/
|
||||||
|
.ansible/
|
||||||
|
|
||||||
|
# Generated inventory (regenerate with setup.sh)
|
||||||
|
inventory/hosts.yml
|
||||||
|
inventory/group_vars/all/vars.yml
|
||||||
|
|
||||||
|
# Keep vault.yml encrypted (DO NOT ignore it - it should be committed encrypted)
|
||||||
|
# inventory/group_vars/all/vault.yml
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
259
ansible/AGENTS.md
Normal file
259
ansible/AGENTS.md
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
# Agent Guidelines for Ansible Infrastructure Repository
|
||||||
|
|
||||||
|
This repository contains Docker Compose infrastructure configurations for deploying self-hosted productivity services. This document provides guidelines for AI coding agents working in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
**Type:** Docker Compose Infrastructure Project
|
||||||
|
**Primary Technology:** Docker, Docker Compose
|
||||||
|
**Purpose:** Deployment configurations for self-hosted productivity stack including Nextcloud, OnlyOffice, Excalidraw, and Obsidian
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/liph/programming/ansible/
|
||||||
|
└── nextcloud/
|
||||||
|
├── docker-compose.yml # Main orchestration file (5 services)
|
||||||
|
└── .env # Environment variables
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Docker Compose Operations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Navigate to service directory
|
||||||
|
cd nextcloud/
|
||||||
|
|
||||||
|
# Start all services
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Start specific service
|
||||||
|
docker compose up -d <service-name>
|
||||||
|
|
||||||
|
# Stop all services
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Stop and remove volumes (DESTRUCTIVE)
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
# View logs for all services
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# View logs for specific service
|
||||||
|
docker compose logs -f <service-name>
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
docker compose restart <service-name>
|
||||||
|
|
||||||
|
# Rebuild and restart service
|
||||||
|
docker compose up -d --build <service-name>
|
||||||
|
|
||||||
|
# Validate docker-compose.yml syntax
|
||||||
|
docker compose config
|
||||||
|
|
||||||
|
# List running services
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Names
|
||||||
|
- `excalidraw` - Drawing/whiteboard tool (port 3009)
|
||||||
|
- `next-db` - PostgreSQL 18 database
|
||||||
|
- `next` - Nextcloud main application (port 8808)
|
||||||
|
- `onlyoffice` - Document server (port 8000)
|
||||||
|
- `obsidian` - Note-taking app (ports 3004-3005)
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
No automated tests exist. Manual testing workflow:
|
||||||
|
1. Validate syntax: `docker compose config`
|
||||||
|
2. Start services: `docker compose up -d`
|
||||||
|
3. Check health: `docker compose ps`
|
||||||
|
4. Review logs: `docker compose logs`
|
||||||
|
5. Test endpoints: `curl localhost:8808` (Nextcloud), etc.
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### YAML Formatting (docker-compose.yml)
|
||||||
|
|
||||||
|
**Indentation:**
|
||||||
|
- Use 2 spaces for indentation (NO tabs)
|
||||||
|
- Maintain consistent indentation levels
|
||||||
|
|
||||||
|
**Structure:**
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
service-name:
|
||||||
|
image: docker.io/image:tag
|
||||||
|
container_name: container-name
|
||||||
|
depends_on:
|
||||||
|
- dependency
|
||||||
|
ports:
|
||||||
|
- "host:container"
|
||||||
|
environment:
|
||||||
|
- KEY=value
|
||||||
|
- KEY=${ENV_VAR}
|
||||||
|
volumes:
|
||||||
|
- volume_name:/container/path
|
||||||
|
- ./host/path:/container/path
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- network_name
|
||||||
|
```
|
||||||
|
|
||||||
|
**Naming Conventions:**
|
||||||
|
- Container names: Use kebab-case (e.g., `next-db`, `next-redis`)
|
||||||
|
- Service names: Use snake_case or kebab-case consistently
|
||||||
|
- Volume names: Use snake_case (e.g., `nextcloud_data`, `pg_data`)
|
||||||
|
- Network names: Use snake_case with project prefix (e.g., `nextcloud_network`)
|
||||||
|
|
||||||
|
**Environment Variables:**
|
||||||
|
- Reference .env variables using `${VAR_NAME}` syntax
|
||||||
|
- Never hardcode sensitive data (passwords, secrets) in docker-compose.yml
|
||||||
|
- Use explicit environment variable declarations with `-` prefix
|
||||||
|
|
||||||
|
**Image Specifications:**
|
||||||
|
- Always use fully qualified image names: `docker.io/image:tag`
|
||||||
|
- Pin specific versions for production (avoid `:latest` in production)
|
||||||
|
- Current exception: development environment uses `:latest` tags
|
||||||
|
|
||||||
|
**Volumes:**
|
||||||
|
- Named volumes for persistent data (defined in volumes section)
|
||||||
|
- Bind mounts for configuration files using `./relative/path`
|
||||||
|
- Always specify volume mount destination path
|
||||||
|
- CRITICAL: Never leave empty volume definitions (e.g., `- :/path`)
|
||||||
|
|
||||||
|
**Comments:**
|
||||||
|
- Use `#` for comments
|
||||||
|
- Add comments above service definitions to describe purpose
|
||||||
|
- Comment out optional services with `# ` prefix on each line
|
||||||
|
|
||||||
|
### Environment Files (.env)
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
```bash
|
||||||
|
# Database Configuration
|
||||||
|
DB_PASSWORD=secure_password_here
|
||||||
|
DB_USERNAME=nextcloud
|
||||||
|
DB_DATABASE_NAME=nextcloud
|
||||||
|
DB_HOST=next-db
|
||||||
|
|
||||||
|
# User/Group IDs
|
||||||
|
PUID=33
|
||||||
|
PGID=1000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rules:**
|
||||||
|
- One variable per line: `KEY=value`
|
||||||
|
- No spaces around `=`
|
||||||
|
- No quotes needed for values
|
||||||
|
- Group related variables with comment headers
|
||||||
|
- Never commit sensitive values (use placeholders or .env.example)
|
||||||
|
- All variables must have values (no empty assignments)
|
||||||
|
|
||||||
|
### Security Guidelines
|
||||||
|
|
||||||
|
1. **Secrets Management:**
|
||||||
|
- Never hardcode passwords in docker-compose.yml
|
||||||
|
- Use .env file for credentials (ensure .env is in .gitignore)
|
||||||
|
- Consider Docker secrets for sensitive production data
|
||||||
|
- Rotate default passwords immediately
|
||||||
|
|
||||||
|
2. **Current Security Issues to Fix:**
|
||||||
|
- Line 53 in docker-compose.yml: Remove hardcoded admin password
|
||||||
|
- .env file: All database credentials are empty
|
||||||
|
- Consider enabling JWT for OnlyOffice (currently disabled)
|
||||||
|
|
||||||
|
3. **Network Security:**
|
||||||
|
- Use isolated Docker networks for service communication
|
||||||
|
- Only expose necessary ports to host
|
||||||
|
- Consider reverse proxy (nginx/traefik) for HTTPS termination
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
**Validation Steps Before Committing:**
|
||||||
|
1. Run `docker compose config` to validate YAML syntax
|
||||||
|
2. Ensure all volume mount paths are complete
|
||||||
|
3. Verify all environment variables are defined in .env
|
||||||
|
4. Check for hardcoded secrets
|
||||||
|
5. Confirm port conflicts with `docker compose ps`
|
||||||
|
|
||||||
|
**Common Issues:**
|
||||||
|
- Empty volume definitions: Always specify source and destination
|
||||||
|
- Missing environment variables: Define all referenced vars in .env
|
||||||
|
- Port conflicts: Use `docker ps` to check existing port bindings
|
||||||
|
- Network errors: Ensure services on same network can communicate
|
||||||
|
|
||||||
|
### Configuration Management
|
||||||
|
|
||||||
|
**Volume Mapping Patterns:**
|
||||||
|
```yaml
|
||||||
|
# Named volume (recommended for data)
|
||||||
|
volumes:
|
||||||
|
- nextcloud_data:/var/www/html
|
||||||
|
|
||||||
|
# Bind mount with SELinux label (for config)
|
||||||
|
volumes:
|
||||||
|
- ./config:/var/www/html/config:Z
|
||||||
|
|
||||||
|
# Bind mount with shared label
|
||||||
|
volumes:
|
||||||
|
- ./vault:/vault:z
|
||||||
|
```
|
||||||
|
|
||||||
|
**Restart Policies:**
|
||||||
|
- Use `restart: unless-stopped` for production services
|
||||||
|
- Avoid `restart: always` (harder to stop deliberately)
|
||||||
|
- Use `restart: on-failure` for development/debugging
|
||||||
|
|
||||||
|
### Database Configuration
|
||||||
|
|
||||||
|
**PostgreSQL Environment Variables:**
|
||||||
|
- `POSTGRES_DB` - Database name
|
||||||
|
- `POSTGRES_USER` - Database user
|
||||||
|
- `POSTGRES_PASSWORD` - Database password
|
||||||
|
|
||||||
|
**Nextcloud Database Connection:**
|
||||||
|
- `POSTGRES_HOST` - Hostname (service name: `next-db`)
|
||||||
|
- Must match database service configuration
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **Service Dependencies:**
|
||||||
|
- Use `depends_on` to define startup order
|
||||||
|
- Note: `depends_on` doesn't wait for "ready" state
|
||||||
|
|
||||||
|
2. **Resource Limits:**
|
||||||
|
- Consider adding memory/CPU limits for production
|
||||||
|
- Current config uses `mem_swappiness: -1` for Nextcloud
|
||||||
|
|
||||||
|
3. **Timezone Configuration:**
|
||||||
|
- Set `TZ` environment variable for correct timestamps
|
||||||
|
- Current: `TZ=Europe/Zurich` (Excalidraw), `TZ=Etc/UTC` (Obsidian)
|
||||||
|
|
||||||
|
4. **Data Persistence:**
|
||||||
|
- Always use named volumes for important data
|
||||||
|
- Define volumes in top-level `volumes:` section
|
||||||
|
- Backup volume data regularly
|
||||||
|
|
||||||
|
5. **Making Changes:**
|
||||||
|
- Test changes in development before production
|
||||||
|
- Use `docker compose config` to validate
|
||||||
|
- Review diff carefully before applying
|
||||||
|
- Document breaking changes
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
1. Make changes to docker-compose.yml or .env
|
||||||
|
2. Validate: `docker compose config`
|
||||||
|
3. Apply changes: `docker compose up -d`
|
||||||
|
4. Verify: `docker compose ps && docker compose logs`
|
||||||
|
5. Test affected services manually
|
||||||
|
6. Document changes in commit message
|
||||||
|
|
||||||
|
## Critical Fixes Needed
|
||||||
|
|
||||||
|
- [ ] Complete empty volume mount paths (lines 21, 55-57, 74, 89-90)
|
||||||
|
- [ ] Remove hardcoded admin password (line 53)
|
||||||
|
- [ ] Populate database credentials in .env file
|
||||||
|
- [ ] Add .gitignore to exclude .env from version control
|
||||||
|
- [ ] Consider adding docker-compose.override.yml for local development
|
||||||
426
ansible/ARCHITECTURE.md
Normal file
426
ansible/ARCHITECTURE.md
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
# Network Architecture
|
||||||
|
|
||||||
|
Visual overview of how all services connect and communicate.
|
||||||
|
|
||||||
|
## 🌐 Network Flow Diagram
|
||||||
|
|
||||||
|
```
|
||||||
|
INTERNET
|
||||||
|
│
|
||||||
|
│ DNS A Records
|
||||||
|
│ (cloud.yourdomain.com → SERVER_IP)
|
||||||
|
↓
|
||||||
|
┌───────────────┐
|
||||||
|
│ Firewall (UFW)│
|
||||||
|
│ Ports: 80 │
|
||||||
|
│ 443 │
|
||||||
|
│ 41641 │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
┌────────────────┼────────────────┐
|
||||||
|
│ │ │
|
||||||
|
Port 80/443 Port 41641 Port 22
|
||||||
|
│ │ │
|
||||||
|
↓ ↓ ↓
|
||||||
|
┌──────────────┐ ┌─────────────┐ ┌──────────┐
|
||||||
|
│ CADDY │ │ TAILSCALE │ │ SSH │
|
||||||
|
│ Reverse Proxy│ │ VPN │ │ Access │
|
||||||
|
└──────┬───────┘ └─────┬───────┘ └──────────┘
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
┌──────────┴────────┐ │
|
||||||
|
│ │ │
|
||||||
|
↓ ↓ ↓
|
||||||
|
┌─────────┐ ┌─────────────────┐
|
||||||
|
│ PUBLIC │ │ TAILSCALE-ONLY │
|
||||||
|
│SERVICES │ │ SERVICES │
|
||||||
|
└─────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Access Matrix
|
||||||
|
|
||||||
|
| Service | Public URL | Tailscale URL | Direct Access |
|
||||||
|
|---------|-----------|---------------|---------------|
|
||||||
|
| **Nextcloud** | ✅ https://cloud.domain.com | ✅ Via Tailscale IP | ❌ No |
|
||||||
|
| **OnlyOffice** | ✅ https://office.domain.com | ✅ Via Tailscale IP | ❌ No |
|
||||||
|
| **Excalidraw** | ✅ https://draw.domain.com | ✅ Via Tailscale IP | ❌ No |
|
||||||
|
| **Obsidian** | ✅ https://notes.domain.com | ✅ Via Tailscale IP | ❌ No |
|
||||||
|
| **Homarr** | ❌ 403 Forbidden | ✅ https://home.domain.com | ❌ No |
|
||||||
|
| **Dockhand** | ❌ 403 Forbidden | ✅ https://manage.domain.com | ❌ No |
|
||||||
|
| **Uptime Kuma** | ❌ 403 Forbidden | ✅ https://uptime.domain.com | ❌ No |
|
||||||
|
| **PostgreSQL** | ❌ No | ❌ No | ✅ Docker network only |
|
||||||
|
| **Redis** | ❌ No | ❌ No | ✅ Docker network only |
|
||||||
|
| **Caddy** | ❌ No (serves others) | ❌ No | ✅ Docker network only |
|
||||||
|
| **Watchtower** | ❌ No | ❌ No | ✅ Docker network only |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker Network Topology
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ nextcloud_network (bridge) │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │PostgreSQL│ │ Redis │ │Nextcloud │ │OnlyOffice│ │
|
||||||
|
│ │ :5432 │ │ :6379 │ │ :80 │ │ :80 │ │
|
||||||
|
│ └──────────┘ └──────────┘ └────┬─────┘ └──────────┘ │
|
||||||
|
│ ↑ ↑ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └──────────────┴─────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌──────────┐ ┌────┴─────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │Excalidraw│ │ Caddy │ │ Obsidian │ │ Homarr │ │
|
||||||
|
│ │ :80 │ │80→:80 │ │ :3000 │ │ :7575 │ │
|
||||||
|
│ └──────────┘ │443→:443 │ └──────────┘ └────┬─────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ ┌──────────┐ │ │ ┌──────────┐ ↓ │
|
||||||
|
│ │ Dockhand │ │ │ │ Uptime │ /var/run/ │
|
||||||
|
│ │ :3000 │ │ │ │ Kuma │ docker.sock │
|
||||||
|
│ └──────────┘ └──────────┘ │ :3001 │ (read-only) │
|
||||||
|
│ └──────────┘ │
|
||||||
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Watchtower (monitors all) │ │
|
||||||
|
│ │ Accesses: /var/run/docker.sock │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
│ │
|
||||||
|
│ Exposed Ports │ Internal
|
||||||
|
│ │ Communication
|
||||||
|
↓ ↓
|
||||||
|
Host: 80, 443 Container-to-Container
|
||||||
|
(Caddy only) via Docker DNS
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Request Flow Examples
|
||||||
|
|
||||||
|
### Example 1: User Accesses Nextcloud
|
||||||
|
|
||||||
|
```
|
||||||
|
User Browser
|
||||||
|
│
|
||||||
|
│ HTTPS GET https://cloud.yourdomain.com
|
||||||
|
↓
|
||||||
|
Internet
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Firewall (Port 443)
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Caddy Container (:443)
|
||||||
|
│
|
||||||
|
│ Validates SSL certificate
|
||||||
|
│ Checks Caddyfile rules
|
||||||
|
│ Proxies to backend
|
||||||
|
↓
|
||||||
|
Nextcloud Container (:80)
|
||||||
|
│
|
||||||
|
│ Checks trusted domains
|
||||||
|
│ Queries PostgreSQL
|
||||||
|
│ Checks Redis cache
|
||||||
|
↓
|
||||||
|
PostgreSQL + Redis
|
||||||
|
│
|
||||||
|
│ Returns data
|
||||||
|
↓
|
||||||
|
Nextcloud → Caddy → User
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: User Accesses Homarr (via Tailscale)
|
||||||
|
|
||||||
|
```
|
||||||
|
User Browser (on Tailscale)
|
||||||
|
│
|
||||||
|
│ HTTPS GET https://home.yourdomain.com
|
||||||
|
↓
|
||||||
|
Internet → DNS Resolution
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Firewall (Port 443)
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Caddy Container
|
||||||
|
│
|
||||||
|
│ Checks client IP: 100.64.x.x (Tailscale range)
|
||||||
|
│ ✅ Match! Proxy to backend
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Homarr Container (:7575)
|
||||||
|
│
|
||||||
|
│ Reads Docker socket
|
||||||
|
│ Shows container status
|
||||||
|
↓
|
||||||
|
Returns dashboard → Caddy → User
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: User Accesses Homarr (NOT on Tailscale)
|
||||||
|
|
||||||
|
```
|
||||||
|
User Browser (public internet)
|
||||||
|
│
|
||||||
|
│ HTTPS GET https://home.yourdomain.com
|
||||||
|
↓
|
||||||
|
Internet → DNS Resolution
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Firewall (Port 443)
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Caddy Container
|
||||||
|
│
|
||||||
|
│ Checks client IP: NOT in 100.64.0.0/10
|
||||||
|
│ ❌ No match! Return 403 Forbidden
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
403 Access Denied
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Nextcloud Connects to Database
|
||||||
|
|
||||||
|
```
|
||||||
|
Nextcloud Container
|
||||||
|
│
|
||||||
|
│ Docker DNS lookup: next-db
|
||||||
|
│ Resolves to: 172.20.0.x
|
||||||
|
↓
|
||||||
|
PostgreSQL Container (next-db:5432)
|
||||||
|
│
|
||||||
|
│ Internal Docker network
|
||||||
|
│ No external exposure
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Returns query results
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Data Flow Diagram
|
||||||
|
|
||||||
|
### File Upload to Nextcloud
|
||||||
|
|
||||||
|
```
|
||||||
|
User → Caddy → Nextcloud → Storage Volume
|
||||||
|
│
|
||||||
|
├→ PostgreSQL (metadata)
|
||||||
|
└→ Redis (cache)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Document Editing with OnlyOffice
|
||||||
|
|
||||||
|
```
|
||||||
|
User → Caddy → Nextcloud → Caddy → OnlyOffice
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
PostgreSQL Document
|
||||||
|
(metadata) Processing
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
Nextcloud ←───────── OnlyOffice
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
Storage Volume
|
||||||
|
```
|
||||||
|
|
||||||
|
### Watchtower Auto-Update
|
||||||
|
|
||||||
|
```
|
||||||
|
Watchtower (cron: every 24h)
|
||||||
|
│
|
||||||
|
│ Check Docker Hub for new images
|
||||||
|
↓
|
||||||
|
New image available?
|
||||||
|
│
|
||||||
|
├─ NO → Sleep
|
||||||
|
│
|
||||||
|
├─ YES → Check container labels
|
||||||
|
│
|
||||||
|
├─ "enable=false" → Skip
|
||||||
|
├─ "enable=true" → Pull & Update
|
||||||
|
└─ "monitor-only=true" → Notify
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Boundaries
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ INTERNET (Untrusted) │
|
||||||
|
└────────────────────────┬────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ UFW Firewall │ ← Layer 1: Host Firewall
|
||||||
|
│ 22, 80, 443 │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ fail2ban │ ← Layer 2: Intrusion Detection
|
||||||
|
│ SSH Protection │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ Caddy (HTTPS) │ ← Layer 3: TLS Termination
|
||||||
|
│ SSL Validation │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────────────┼────────────────┐
|
||||||
|
│ │ │
|
||||||
|
┌───▼────┐ ┌────▼────┐ ┌─────▼─────┐
|
||||||
|
│ Public │ │Tailscale│ │ Docker │ ← Layer 4: Network
|
||||||
|
│Services│ │ Check │ │ Network │ Isolation
|
||||||
|
└────────┘ └─────────┘ └───────────┘
|
||||||
|
│
|
||||||
|
┌────▼────┐
|
||||||
|
│ Admin │ ← Layer 5: Admin Access
|
||||||
|
│ Services│ (Tailscale VPN only)
|
||||||
|
└─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗄️ Data Persistence
|
||||||
|
|
||||||
|
### Docker Volumes
|
||||||
|
|
||||||
|
```
|
||||||
|
Host Filesystem
|
||||||
|
│
|
||||||
|
├── /var/lib/docker/volumes/
|
||||||
|
│ ├── nextcloud_pg_data/ ← PostgreSQL database
|
||||||
|
│ ├── nextcloud_redis_data/ ← Redis cache
|
||||||
|
│ ├── nextcloud_nextcloud_data/ ← Nextcloud files
|
||||||
|
│ ├── nextcloud_nextcloud_apps/ ← Nextcloud apps
|
||||||
|
│ ├── nextcloud_onlyoffice_data/ ← OnlyOffice docs
|
||||||
|
│ ├── nextcloud_caddy_data/ ← SSL certificates
|
||||||
|
│ ├── nextcloud_caddy_config/ ← Caddy config
|
||||||
|
│ ├── nextcloud_dockhand_data/ ← Dockhand settings
|
||||||
|
│ └── nextcloud_uptime_kuma_data/ ← Uptime Kuma data
|
||||||
|
│
|
||||||
|
└── /opt/nextcloud-stack/
|
||||||
|
├── configs/
|
||||||
|
│ ├── caddy/Caddyfile
|
||||||
|
│ └── nextcloud/
|
||||||
|
├── data/
|
||||||
|
│ ├── homarr/
|
||||||
|
│ └── obsidian/
|
||||||
|
└── backups/
|
||||||
|
├── database/
|
||||||
|
└── volumes/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Update Mechanism
|
||||||
|
|
||||||
|
### Watchtower Label-Based Control
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Auto-update allowed
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
→ Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
|
||||||
|
|
||||||
|
# Monitor only (notify, no update)
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.monitor-only=true"
|
||||||
|
→ OnlyOffice
|
||||||
|
|
||||||
|
# Never auto-update (manual only)
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
|
→ Nextcloud, PostgreSQL, Redis
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📡 Monitoring Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────┐
|
||||||
|
│ Uptime Kuma │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ HTTP Monitors (every 60s) │ │
|
||||||
|
│ │ • https://cloud.yourdomain.com/status.php │ │
|
||||||
|
│ │ • https://office.yourdomain.com/health │ │
|
||||||
|
│ │ • https://draw.yourdomain.com │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ TCP Port Monitors │ │
|
||||||
|
│ │ • next-db:5432 (PostgreSQL) │ │
|
||||||
|
│ │ • next-redis:6379 (Redis) │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ SSL Certificate Monitor │ │
|
||||||
|
│ │ • Expiry check (30-day warning) │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ Notifications │ │
|
||||||
|
│ │ • Email alerts │ │
|
||||||
|
│ │ • Slack/Discord webhooks (optional) │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
└────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Service Dependencies
|
||||||
|
|
||||||
|
```
|
||||||
|
Nextcloud
|
||||||
|
│
|
||||||
|
├─ requires → PostgreSQL (healthy)
|
||||||
|
├─ requires → Redis (healthy)
|
||||||
|
└─ requires → Caddy (for external access)
|
||||||
|
|
||||||
|
OnlyOffice
|
||||||
|
│
|
||||||
|
└─ requires → Caddy (for external access)
|
||||||
|
|
||||||
|
Homarr, Dockhand
|
||||||
|
│
|
||||||
|
├─ requires → Docker Socket (read-only)
|
||||||
|
└─ requires → Caddy (for Tailscale access)
|
||||||
|
|
||||||
|
Watchtower
|
||||||
|
│
|
||||||
|
└─ requires → Docker Socket (read-write)
|
||||||
|
|
||||||
|
All Services
|
||||||
|
│
|
||||||
|
└─ network → nextcloud_network
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌟 Architecture Benefits
|
||||||
|
|
||||||
|
### High Availability Features
|
||||||
|
- **Health checks**: Containers auto-restart on failure
|
||||||
|
- **Database connection pooling**: Redis caching reduces DB load
|
||||||
|
- **SSL auto-renewal**: Caddy handles certificate lifecycle
|
||||||
|
|
||||||
|
### Security Layers
|
||||||
|
1. **Network**: UFW firewall + fail2ban
|
||||||
|
2. **Transport**: TLS/SSL via Caddy
|
||||||
|
3. **Access**: Tailscale VPN for admin interfaces
|
||||||
|
4. **Secrets**: Ansible Vault encryption
|
||||||
|
5. **Isolation**: Docker network segmentation
|
||||||
|
|
||||||
|
### Scalability Points
|
||||||
|
- **Database**: Can migrate to external PostgreSQL
|
||||||
|
- **Cache**: Redis can be scaled/clustered
|
||||||
|
- **Storage**: Volumes can be moved to network storage
|
||||||
|
- **Reverse Proxy**: Caddy can be load-balanced
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**This architecture provides a secure, maintainable, and production-ready self-hosted cloud platform.**
|
||||||
420
ansible/BACKUP_RESTORE.md
Normal file
420
ansible/BACKUP_RESTORE.md
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
# Backup and Restore Guide
|
||||||
|
|
||||||
|
Complete guide for backing up and restoring your Nextcloud Stack.
|
||||||
|
|
||||||
|
## Automated Backups
|
||||||
|
|
||||||
|
### What Gets Backed Up
|
||||||
|
|
||||||
|
- **Daily (3:00 AM)**:
|
||||||
|
- PostgreSQL database dump
|
||||||
|
|
||||||
|
- **Weekly (Sundays, 3:00 AM)**:
|
||||||
|
- Nextcloud data volume
|
||||||
|
- Configuration files
|
||||||
|
|
||||||
|
- **Retention**: 30 days
|
||||||
|
|
||||||
|
### Backup Locations
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/nextcloud-stack/backups/
|
||||||
|
├── database/
|
||||||
|
│ └── nextcloud_db_YYYYMMDD_HHMMSS.sql.gz
|
||||||
|
├── volumes/
|
||||||
|
│ ├── nextcloud_data_YYYYMMDD_HHMMSS.tar.gz
|
||||||
|
│ └── configs_YYYYMMDD_HHMMSS.tar.gz
|
||||||
|
└── backup.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
/opt/nextcloud-stack/backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Backup Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View backup log
|
||||||
|
tail -f /opt/nextcloud-stack/backups/backup.log
|
||||||
|
|
||||||
|
# List backups
|
||||||
|
ls -lh /opt/nextcloud-stack/backups/database/
|
||||||
|
ls -lh /opt/nextcloud-stack/backups/volumes/
|
||||||
|
|
||||||
|
# Check disk usage
|
||||||
|
du -sh /opt/nextcloud-stack/backups/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Restore Procedures
|
||||||
|
|
||||||
|
### 1. Restore Database Only
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Database corruption
|
||||||
|
- Accidental data deletion
|
||||||
|
- Need to revert to earlier state
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Stop Nextcloud (keep database running)
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose stop next
|
||||||
|
|
||||||
|
# 2. List available backups
|
||||||
|
ls -lh backups/database/
|
||||||
|
|
||||||
|
# 3. Choose backup to restore
|
||||||
|
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
|
||||||
|
|
||||||
|
# 4. Drop existing database (CAUTION!)
|
||||||
|
docker exec next-db psql -U nextcloud -c "DROP DATABASE nextcloud;"
|
||||||
|
docker exec next-db psql -U nextcloud -c "CREATE DATABASE nextcloud;"
|
||||||
|
|
||||||
|
# 5. Restore from backup
|
||||||
|
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
|
||||||
|
|
||||||
|
# 6. Restart Nextcloud
|
||||||
|
docker compose start next
|
||||||
|
|
||||||
|
# 7. Run Nextcloud upgrade (if needed)
|
||||||
|
docker exec -u www-data next php occ upgrade
|
||||||
|
|
||||||
|
# 8. Disable maintenance mode
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Restore Data Volumes
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Lost files
|
||||||
|
- Corrupted data directory
|
||||||
|
- Complete system failure
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Stop all services
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# 2. List available backups
|
||||||
|
ls -lh backups/volumes/
|
||||||
|
|
||||||
|
# 3. Choose backup
|
||||||
|
BACKUP_FILE="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
|
||||||
|
|
||||||
|
# 4. Restore volume
|
||||||
|
# Note: This requires accessing the Docker volume directory
|
||||||
|
sudo tar -xzf $BACKUP_FILE -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
|
||||||
|
|
||||||
|
# 5. Restore configs (optional)
|
||||||
|
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
|
||||||
|
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
|
||||||
|
|
||||||
|
# 6. Fix permissions
|
||||||
|
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
|
||||||
|
|
||||||
|
# 7. Start services
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 8. Run Nextcloud file scan
|
||||||
|
docker exec -u www-data next php occ files:scan --all
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Complete System Restore
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Disaster recovery
|
||||||
|
- Migration to new server
|
||||||
|
- Complete system failure
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. On new server, install base system
|
||||||
|
# Run Ansible playbooks 01-04 (up to Tailscale)
|
||||||
|
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
|
||||||
|
ansible-playbook playbooks/02-system-setup.yml --ask-vault-pass
|
||||||
|
ansible-playbook playbooks/03-docker-setup.yml --ask-vault-pass
|
||||||
|
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# 2. Create deployment directory structure
|
||||||
|
ssh user@new-server
|
||||||
|
sudo mkdir -p /opt/nextcloud-stack/backups/{database,volumes}
|
||||||
|
|
||||||
|
# 3. Copy backups from old server to new server
|
||||||
|
# On your local machine:
|
||||||
|
scp -r old-server:/opt/nextcloud-stack/backups/* user@new-server:/opt/nextcloud-stack/backups/
|
||||||
|
|
||||||
|
# 4. Deploy stack (creates volumes and containers)
|
||||||
|
ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# 5. Stop services on new server
|
||||||
|
ssh user@new-server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# 6. Restore database
|
||||||
|
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
|
||||||
|
docker compose up -d next-db next-redis
|
||||||
|
sleep 10
|
||||||
|
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
|
||||||
|
|
||||||
|
# 7. Restore data volumes
|
||||||
|
VOLUME_BACKUP="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
|
||||||
|
sudo tar -xzf $VOLUME_BACKUP -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
|
||||||
|
|
||||||
|
# 8. Restore configs
|
||||||
|
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
|
||||||
|
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
|
||||||
|
|
||||||
|
# 9. Fix permissions
|
||||||
|
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
|
||||||
|
|
||||||
|
# 10. Start all services
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 11. Run Nextcloud maintenance
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
docker exec -u www-data next php occ files:scan --all
|
||||||
|
docker exec -u www-data next php occ db:add-missing-indices
|
||||||
|
|
||||||
|
# 12. Continue with remaining playbooks
|
||||||
|
exit
|
||||||
|
ansible-playbook playbooks/06-configure-caddy.yml --ask-vault-pass
|
||||||
|
ansible-playbook playbooks/07-setup-backups.yml --ask-vault-pass
|
||||||
|
ansible-playbook playbooks/08-post-deployment.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Off-Site Backups with rclone
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
|
||||||
|
# Configure rclone
|
||||||
|
rclone config
|
||||||
|
|
||||||
|
# Example: Setup Backblaze B2
|
||||||
|
# Follow prompts:
|
||||||
|
# - Choose: New remote
|
||||||
|
# - Name: b2backup
|
||||||
|
# - Storage: Backblaze B2
|
||||||
|
# - Enter account ID and application key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync Backups to Remote
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test sync (dry run)
|
||||||
|
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups --dry-run
|
||||||
|
|
||||||
|
# Actual sync
|
||||||
|
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Off-Site Backups
|
||||||
|
|
||||||
|
Add to `/opt/nextcloud-stack/backup.sh` (after the cleanup section):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Off-site backup (if rclone configured)
|
||||||
|
if command -v rclone &> /dev/null && rclone listremotes | grep -q "b2backup"; then
|
||||||
|
log "Syncing backups to off-site storage..."
|
||||||
|
rclone sync $BACKUP_DIR b2backup:nextcloud-backups --log-file=$LOG_FILE
|
||||||
|
log "Off-site sync completed"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore from Off-Site Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List remote backups
|
||||||
|
rclone ls b2backup:nextcloud-backups/database/
|
||||||
|
|
||||||
|
# Download specific backup
|
||||||
|
rclone copy b2backup:nextcloud-backups/database/nextcloud_db_20260216_030000.sql.gz /opt/nextcloud-stack/backups/database/
|
||||||
|
|
||||||
|
# Download all backups
|
||||||
|
rclone sync b2backup:nextcloud-backups /opt/nextcloud-stack/backups/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Verification
|
||||||
|
|
||||||
|
### Test Database Backup Integrity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Extract backup
|
||||||
|
gunzip -c backups/database/nextcloud_db_20260216_030000.sql.gz > /tmp/test_restore.sql
|
||||||
|
|
||||||
|
# 2. Check for errors
|
||||||
|
grep -i "error" /tmp/test_restore.sql
|
||||||
|
|
||||||
|
# 3. Verify size (should be reasonable)
|
||||||
|
ls -lh /tmp/test_restore.sql
|
||||||
|
|
||||||
|
# 4. Cleanup
|
||||||
|
rm /tmp/test_restore.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Volume Backup Integrity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test tar archive
|
||||||
|
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz | head -20
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz > /dev/null && echo "Archive OK"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration to New Server
|
||||||
|
|
||||||
|
**Complete migration guide:**
|
||||||
|
|
||||||
|
1. **Prepare new server:**
|
||||||
|
- Same or newer Ubuntu version
|
||||||
|
- Run Ansible setup playbooks (01-04)
|
||||||
|
|
||||||
|
2. **Backup old server:**
|
||||||
|
```bash
|
||||||
|
/opt/nextcloud-stack/backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Copy backups to new server:**
|
||||||
|
```bash
|
||||||
|
rsync -avz old-server:/opt/nextcloud-stack/backups/ new-server:/opt/nextcloud-stack/backups/
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update DNS:**
|
||||||
|
- Point all domains to new server IP
|
||||||
|
- Wait for propagation
|
||||||
|
|
||||||
|
5. **Restore on new server:**
|
||||||
|
- Follow "Complete System Restore" steps above
|
||||||
|
|
||||||
|
6. **Verify:**
|
||||||
|
- Test all services
|
||||||
|
- Check Nextcloud functionality
|
||||||
|
- Verify SSL certificates
|
||||||
|
|
||||||
|
7. **Decommission old server:**
|
||||||
|
- Only after confirming new server works
|
||||||
|
- Keep final backup from old server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Best Practices
|
||||||
|
|
||||||
|
### DO:
|
||||||
|
- ✅ Test restores regularly (monthly)
|
||||||
|
- ✅ Keep backups off-site (rclone to cloud storage)
|
||||||
|
- ✅ Verify backup integrity
|
||||||
|
- ✅ Monitor backup log for errors
|
||||||
|
- ✅ Document restore procedures
|
||||||
|
- ✅ Keep multiple backup generations
|
||||||
|
|
||||||
|
### DON'T:
|
||||||
|
- ❌ Store backups only on same server
|
||||||
|
- ❌ Assume backups work without testing
|
||||||
|
- ❌ Delete old backups without verification
|
||||||
|
- ❌ Skip database backups
|
||||||
|
- ❌ Ignore backup failure notifications
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backup Schedule Customization
|
||||||
|
|
||||||
|
Edit `/opt/nextcloud-stack/backup.sh` to change:
|
||||||
|
|
||||||
|
- Backup frequency
|
||||||
|
- Retention period
|
||||||
|
- What gets backed up
|
||||||
|
- Compression settings
|
||||||
|
|
||||||
|
Edit cron job:
|
||||||
|
```bash
|
||||||
|
sudo crontab -e
|
||||||
|
|
||||||
|
# Change from 3:00 AM to 2:00 AM
|
||||||
|
0 2 * * * /opt/nextcloud-stack/backup.sh >> /opt/nextcloud-stack/backups/backup.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting Backups
|
||||||
|
|
||||||
|
### Backup script fails
|
||||||
|
|
||||||
|
**Check logs:**
|
||||||
|
```bash
|
||||||
|
tail -100 /opt/nextcloud-stack/backups/backup.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common issues:**
|
||||||
|
- Disk space full
|
||||||
|
- Docker container not running
|
||||||
|
- Permission denied
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
```bash
|
||||||
|
# Check disk space
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# Check containers
|
||||||
|
docker ps
|
||||||
|
|
||||||
|
# Fix permissions
|
||||||
|
sudo chown -R root:root /opt/nextcloud-stack/backup.sh
|
||||||
|
sudo chmod +x /opt/nextcloud-stack/backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database backup empty
|
||||||
|
|
||||||
|
**Verify database is running:**
|
||||||
|
```bash
|
||||||
|
docker exec next-db pg_isready -U nextcloud
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test manual backup:**
|
||||||
|
```bash
|
||||||
|
docker exec next-db pg_dump -U nextcloud nextcloud > /tmp/test.sql
|
||||||
|
ls -lh /tmp/test.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Emergency Recovery
|
||||||
|
|
||||||
|
If all backups are lost and you need to start fresh:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Remove everything
|
||||||
|
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
# 2. Redeploy from scratch
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# 3. Reconfigure Nextcloud manually
|
||||||
|
# Login and set up users, apps, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Remember:** Backups are worthless unless you've tested restoring from them!
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-16
|
||||||
470
ansible/DEPLOYMENT_GUIDE.md
Normal file
470
ansible/DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
# Quick Deployment Guide
|
||||||
|
|
||||||
|
Get your Nextcloud Stack running in under an hour!
|
||||||
|
|
||||||
|
## ⚡ Quick Start (3 Steps)
|
||||||
|
|
||||||
|
### Step 1: Configure (5-10 minutes)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Answer the questions about your servers and preferences. This generates your encrypted configuration.
|
||||||
|
|
||||||
|
### Step 2: Deploy (30-45 minutes per server)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Access Your Services
|
||||||
|
|
||||||
|
- **Nextcloud**: `https://cloud.yourdomain.com`
|
||||||
|
- **OnlyOffice**: `https://office.yourdomain.com`
|
||||||
|
- **Excalidraw**: `https://draw.yourdomain.com`
|
||||||
|
- **Obsidian**: `https://notes.yourdomain.com`
|
||||||
|
|
||||||
|
**Management (Tailscale only)**:
|
||||||
|
- **Homarr Dashboard**: `https://home.yourdomain.com`
|
||||||
|
- **Dockhand**: `https://manage.yourdomain.com`
|
||||||
|
- **Uptime Kuma**: `https://uptime.yourdomain.com`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Prerequisites Checklist
|
||||||
|
|
||||||
|
### Before You Begin
|
||||||
|
|
||||||
|
- [ ] Ubuntu 20.04+ server (LXC or VPS)
|
||||||
|
- [ ] Root or sudo access
|
||||||
|
- [ ] Minimum 100GB disk space
|
||||||
|
- [ ] Ansible 2.14+ on your control machine
|
||||||
|
- [ ] Domain name with DNS access
|
||||||
|
|
||||||
|
### DNS Configuration (CRITICAL!)
|
||||||
|
|
||||||
|
Before deployment, create these A records pointing to your server IP:
|
||||||
|
|
||||||
|
```
|
||||||
|
cloud.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
office.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
draw.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
notes.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
home.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
manage.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
uptime.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verify DNS before deployment:**
|
||||||
|
```bash
|
||||||
|
dig +short cloud.yourdomain.com
|
||||||
|
# Should return: YOUR_SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 What Gets Deployed
|
||||||
|
|
||||||
|
### 12 Services
|
||||||
|
|
||||||
|
| Service | Purpose | Auto-Update |
|
||||||
|
|---------|---------|-------------|
|
||||||
|
| **Nextcloud** | File sync & collaboration | ❌ Manual |
|
||||||
|
| **PostgreSQL 18** | Database | ❌ Manual |
|
||||||
|
| **Redis 7** | Cache | ❌ Manual |
|
||||||
|
| **OnlyOffice** | Document editor | 👁️ Monitor |
|
||||||
|
| **Excalidraw** | Whiteboard | ✅ Auto |
|
||||||
|
| **Obsidian** | Notes | ✅ Auto |
|
||||||
|
| **Caddy** | Reverse proxy + SSL | ✅ Auto |
|
||||||
|
| **Homarr** | Dashboard | ✅ Auto |
|
||||||
|
| **Dockhand** | Container manager | ✅ Auto |
|
||||||
|
| **Uptime Kuma** | Monitoring | ✅ Auto |
|
||||||
|
| **Watchtower** | Auto-updater | N/A |
|
||||||
|
|
||||||
|
### Security Features
|
||||||
|
|
||||||
|
- ✅ UFW firewall configured
|
||||||
|
- ✅ Fail2ban for SSH protection
|
||||||
|
- ✅ Automatic SSL certificates (Let's Encrypt)
|
||||||
|
- ✅ Ansible Vault encrypted secrets
|
||||||
|
- ✅ Tailscale-only admin interfaces
|
||||||
|
- ✅ Automatic security updates
|
||||||
|
|
||||||
|
### Backup System
|
||||||
|
|
||||||
|
- **Daily**: Database backups (3:00 AM)
|
||||||
|
- **Weekly**: Volume backups (Sundays)
|
||||||
|
- **Retention**: 30 days
|
||||||
|
- **Location**: `/opt/nextcloud-stack/backups/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Detailed Setup Process
|
||||||
|
|
||||||
|
### 1. Install Ansible (Control Machine)
|
||||||
|
|
||||||
|
**Ubuntu/Debian:**
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install ansible
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS:**
|
||||||
|
```bash
|
||||||
|
brew install ansible
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verify:**
|
||||||
|
```bash
|
||||||
|
ansible --version
|
||||||
|
# Should be 2.14 or higher
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Clone Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <your-repo-url>
|
||||||
|
cd ansible-nextcloud-deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run Interactive Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x setup.sh
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**You'll be asked for:**
|
||||||
|
|
||||||
|
1. **Server Details** (for each server):
|
||||||
|
- IP address
|
||||||
|
- Hostname
|
||||||
|
- Domain (e.g., `example.com`)
|
||||||
|
- SSH user
|
||||||
|
- SSH key path
|
||||||
|
|
||||||
|
2. **User Information**:
|
||||||
|
- Your name
|
||||||
|
- Your email (for SSL certificates)
|
||||||
|
- Timezone
|
||||||
|
|
||||||
|
3. **Admin Account**:
|
||||||
|
- Nextcloud admin username
|
||||||
|
- Nextcloud admin password
|
||||||
|
|
||||||
|
4. **Subdomain Preferences** (defaults shown):
|
||||||
|
- Nextcloud: `cloud`
|
||||||
|
- OnlyOffice: `office`
|
||||||
|
- Excalidraw: `draw`
|
||||||
|
- Obsidian: `notes`
|
||||||
|
- Homarr: `home`
|
||||||
|
- Dockhand: `manage`
|
||||||
|
- Uptime Kuma: `uptime`
|
||||||
|
|
||||||
|
5. **Tailscale** (optional):
|
||||||
|
- Auth key (or skip for manual activation)
|
||||||
|
|
||||||
|
6. **Monitoring**:
|
||||||
|
- Alert email
|
||||||
|
- Public status page (yes/no)
|
||||||
|
|
||||||
|
7. **Backups**:
|
||||||
|
- Install rclone (yes/no)
|
||||||
|
|
||||||
|
8. **Ansible Vault Password**:
|
||||||
|
- Create a password to encrypt secrets
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- `inventory/hosts.yml` - Server inventory
|
||||||
|
- `inventory/group_vars/all/vars.yml` - Public variables
|
||||||
|
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
|
||||||
|
|
||||||
|
### 4. Test Connectivity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make ping
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
```bash
|
||||||
|
ansible all -m ping --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
server-hostname | SUCCESS => {
|
||||||
|
"ping": "pong"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Run Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**What happens** (30-45 minutes):
|
||||||
|
|
||||||
|
1. ✅ **Preflight Checks** (2 min)
|
||||||
|
- Validates DNS, connectivity, disk space
|
||||||
|
|
||||||
|
2. ⚙️ **System Setup** (10-15 min)
|
||||||
|
- Installs packages
|
||||||
|
- Configures firewall (UFW)
|
||||||
|
- Sets up fail2ban
|
||||||
|
|
||||||
|
3. 🐳 **Docker Setup** (5 min)
|
||||||
|
- Removes old Docker
|
||||||
|
- Installs Docker CE + Compose v2
|
||||||
|
|
||||||
|
4. 🔐 **Tailscale** (2 min)
|
||||||
|
- Installs Tailscale
|
||||||
|
- Activates if auth key provided
|
||||||
|
|
||||||
|
5. 📦 **Deploy Stack** (10-15 min)
|
||||||
|
- Templates configuration files
|
||||||
|
- Pulls Docker images
|
||||||
|
- Starts all containers
|
||||||
|
- Initializes Nextcloud
|
||||||
|
|
||||||
|
6. 🌐 **Configure Caddy** (2-5 min)
|
||||||
|
- Validates configuration
|
||||||
|
- Obtains SSL certificates
|
||||||
|
|
||||||
|
7. 💾 **Setup Backups** (2 min)
|
||||||
|
- Creates backup script
|
||||||
|
- Sets up cron jobs
|
||||||
|
|
||||||
|
8. ✅ **Post-Deployment** (5 min)
|
||||||
|
- Verifies all services
|
||||||
|
- Installs Nextcloud apps
|
||||||
|
- Creates deployment report
|
||||||
|
|
||||||
|
### 6. Access Your Services
|
||||||
|
|
||||||
|
**Immediately after deployment:**
|
||||||
|
|
||||||
|
1. **Nextcloud**: `https://cloud.yourdomain.com`
|
||||||
|
- Login with admin credentials from setup
|
||||||
|
|
||||||
|
2. **Setup Uptime Kuma** (via Tailscale):
|
||||||
|
- `https://uptime.yourdomain.com`
|
||||||
|
- Create admin account on first visit
|
||||||
|
- Add monitors for your services
|
||||||
|
|
||||||
|
3. **Configure Homarr** (via Tailscale):
|
||||||
|
- `https://home.yourdomain.com`
|
||||||
|
- Add service tiles
|
||||||
|
- Customize dashboard
|
||||||
|
|
||||||
|
4. **OnlyOffice Integration**:
|
||||||
|
- Nextcloud → Settings → Administration → Office
|
||||||
|
- Document Server: `https://office.yourdomain.com`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Post-Deployment Tasks
|
||||||
|
|
||||||
|
### Activate Tailscale (if not done automatically)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
sudo tailscale up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Backups
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make backup
|
||||||
|
```
|
||||||
|
|
||||||
|
Or on the server:
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
/opt/nextcloud-stack/backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Service Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make status
|
||||||
|
```
|
||||||
|
|
||||||
|
Or on the server:
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make logs
|
||||||
|
```
|
||||||
|
|
||||||
|
Or on the server:
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Learning Ansible Commands
|
||||||
|
|
||||||
|
### Useful Makefile Shortcuts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make help # Show all commands
|
||||||
|
make ping # Test connectivity
|
||||||
|
make check # Run preflight checks only
|
||||||
|
make deploy # Full deployment
|
||||||
|
make deploy-dry # Test without making changes
|
||||||
|
make status # Show container status
|
||||||
|
make logs # View all logs
|
||||||
|
make backup # Run manual backup
|
||||||
|
make restart # Restart all containers
|
||||||
|
make edit-vault # Edit encrypted secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Ansible Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run specific playbook
|
||||||
|
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# Run on specific server
|
||||||
|
ansible-playbook playbooks/site.yml --limit server1 --ask-vault-pass
|
||||||
|
|
||||||
|
# Dry run (no changes)
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass --check
|
||||||
|
|
||||||
|
# View inventory
|
||||||
|
ansible-inventory --list
|
||||||
|
|
||||||
|
# Run ad-hoc command
|
||||||
|
ansible all -m shell -a "docker ps" --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 Troubleshooting
|
||||||
|
|
||||||
|
### DNS Not Configured
|
||||||
|
|
||||||
|
**Symptom**: Let's Encrypt fails
|
||||||
|
|
||||||
|
**Solution**: Ensure all DNS A records point to server IP
|
||||||
|
```bash
|
||||||
|
dig +short cloud.yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### LXC Container Issues
|
||||||
|
|
||||||
|
**Symptom**: Docker won't start
|
||||||
|
|
||||||
|
**Solution** (on LXC host):
|
||||||
|
```bash
|
||||||
|
lxc config set CONTAINER_NAME security.nesting true
|
||||||
|
lxc restart CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
|
||||||
|
**Symptom**: Caddy fails to start
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop apache2
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full troubleshooting guide:
|
||||||
|
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Additional Documentation
|
||||||
|
|
||||||
|
- [README.md](README.md) - Complete project overview
|
||||||
|
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions
|
||||||
|
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup and restore procedures
|
||||||
|
- [AGENTS.md](AGENTS.md) - Development guidelines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Security Best Practices
|
||||||
|
|
||||||
|
### After Deployment
|
||||||
|
|
||||||
|
1. **Change default passwords** (if you used simple ones during testing)
|
||||||
|
2. **Enable 2FA** in Nextcloud
|
||||||
|
3. **Review firewall rules**: `sudo ufw status`
|
||||||
|
4. **Check fail2ban**: `sudo fail2ban-client status sshd`
|
||||||
|
5. **Verify SSL certificates**: Visit all your domains in browser
|
||||||
|
6. **Test backups**: Ensure they're working
|
||||||
|
7. **Setup monitoring alerts** in Uptime Kuma
|
||||||
|
|
||||||
|
### Ongoing Maintenance
|
||||||
|
|
||||||
|
- **Weekly**: Check Uptime Kuma for service health
|
||||||
|
- **Monthly**: Test backup restore
|
||||||
|
- **Quarterly**: Review security updates
|
||||||
|
- **Annually**: Rotate passwords
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Getting Help
|
||||||
|
|
||||||
|
1. **Check logs:**
|
||||||
|
```bash
|
||||||
|
docker compose logs [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Deployment report:**
|
||||||
|
```bash
|
||||||
|
cat /opt/nextcloud-stack/DEPLOYMENT.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **System status:**
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
sudo systemctl status docker
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Success Checklist
|
||||||
|
|
||||||
|
After deployment, verify:
|
||||||
|
|
||||||
|
- [ ] All containers running: `docker compose ps`
|
||||||
|
- [ ] Nextcloud accessible via HTTPS
|
||||||
|
- [ ] SSL certificate valid (green padlock in browser)
|
||||||
|
- [ ] Can login to Nextcloud
|
||||||
|
- [ ] OnlyOffice integration working
|
||||||
|
- [ ] Uptime Kuma showing all services green
|
||||||
|
- [ ] Homarr dashboard accessible
|
||||||
|
- [ ] Backup script runs successfully
|
||||||
|
- [ ] Tailscale connected (if using)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**You're all set! Enjoy your self-hosted Nextcloud stack! 🎉**
|
||||||
|
|
||||||
|
For questions or issues, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-16
|
||||||
61
ansible/Makefile
Normal file
61
ansible/Makefile
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Makefile for Nextcloud Stack Deployment
|
||||||
|
# Provides convenient shortcuts for common Ansible operations
|
||||||
|
|
||||||
|
.PHONY: help setup ping check deploy deploy-stack deploy-dry update backup logs status restart rollback clean edit-vault rekey-vault
|
||||||
|
|
||||||
|
help: ## Show this help message
|
||||||
|
@echo "Nextcloud Stack Deployment - Make Commands"
|
||||||
|
@echo ""
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
setup: ## Run interactive setup script
|
||||||
|
@./setup.sh
|
||||||
|
|
||||||
|
ping: ## Test connectivity to all servers
|
||||||
|
@ansible all -m ping --ask-vault-pass
|
||||||
|
|
||||||
|
check: ## Run preflight checks only
|
||||||
|
@ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
|
||||||
|
|
||||||
|
deploy: ## Full deployment (all playbooks)
|
||||||
|
@ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
deploy-stack: ## Deploy stack only (skip system setup)
|
||||||
|
@ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
|
||||||
|
|
||||||
|
deploy-dry: ## Dry run (check mode)
|
||||||
|
@ansible-playbook playbooks/site.yml --ask-vault-pass --check
|
||||||
|
|
||||||
|
update: ## Update Docker images (safe services only)
|
||||||
|
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose pull && docker compose up -d" --ask-vault-pass
|
||||||
|
|
||||||
|
backup: ## Run manual backup on all servers
|
||||||
|
@ansible all -m shell -a "/opt/nextcloud-stack/backup.sh" --ask-vault-pass
|
||||||
|
|
||||||
|
logs: ## Tail logs from all containers
|
||||||
|
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose logs -f --tail=100" --ask-vault-pass
|
||||||
|
|
||||||
|
status: ## Show container status
|
||||||
|
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose ps" --ask-vault-pass
|
||||||
|
|
||||||
|
restart: ## Restart all containers
|
||||||
|
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose restart" --ask-vault-pass
|
||||||
|
|
||||||
|
rollback: ## Emergency rollback
|
||||||
|
@ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
|
||||||
|
|
||||||
|
clean: ## Remove generated inventory (keeps vault)
|
||||||
|
@rm -f inventory/hosts.yml inventory/group_vars/all/vars.yml
|
||||||
|
@echo "Cleaned generated files (vault.yml preserved)"
|
||||||
|
|
||||||
|
edit-vault: ## Edit encrypted vault
|
||||||
|
@ansible-vault edit inventory/group_vars/all/vault.yml
|
||||||
|
|
||||||
|
rekey-vault: ## Change vault password
|
||||||
|
@ansible-vault rekey inventory/group_vars/all/vault.yml
|
||||||
|
|
||||||
|
inventory: ## Show inventory configuration
|
||||||
|
@ansible-inventory --list
|
||||||
|
|
||||||
|
graph: ## Show inventory graph
|
||||||
|
@ansible-inventory --graph
|
||||||
560
ansible/PROJECT_SUMMARY.md
Normal file
560
ansible/PROJECT_SUMMARY.md
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
# Nextcloud Stack - Ansible Deployment Project
|
||||||
|
|
||||||
|
## 🎉 Project Complete!
|
||||||
|
|
||||||
|
This Ansible automation project provides complete, one-command deployment of a self-hosted Nextcloud productivity stack to Ubuntu-based LXC/VPS servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 What's Included
|
||||||
|
|
||||||
|
### Core Files
|
||||||
|
|
||||||
|
```
|
||||||
|
ansible-nextcloud-deployment/
|
||||||
|
├── setup.sh # ⭐ Interactive configuration wizard
|
||||||
|
├── Makefile # Convenient command shortcuts
|
||||||
|
├── ansible.cfg # Ansible configuration
|
||||||
|
├── .gitignore # Git ignore rules
|
||||||
|
│
|
||||||
|
├── inventory/ # Generated by setup.sh
|
||||||
|
│ ├── hosts.yml # Server inventory (generated)
|
||||||
|
│ └── group_vars/all/
|
||||||
|
│ ├── vars.yml # Public variables (generated)
|
||||||
|
│ └── vault.yml # Encrypted secrets (generated)
|
||||||
|
│
|
||||||
|
├── playbooks/ # ⭐ Deployment playbooks
|
||||||
|
│ ├── site.yml # Main orchestrator
|
||||||
|
│ ├── 01-preflight-checks.yml # DNS, connectivity validation
|
||||||
|
│ ├── 02-system-setup.yml # Packages, firewall, security
|
||||||
|
│ ├── 03-docker-setup.yml # Docker CE installation
|
||||||
|
│ ├── 04-tailscale-setup.yml # VPN setup
|
||||||
|
│ ├── 05-deploy-stack.yml # Docker Compose deployment
|
||||||
|
│ ├── 06-configure-caddy.yml # Reverse proxy + SSL
|
||||||
|
│ ├── 07-setup-backups.yml # Backup automation
|
||||||
|
│ ├── 08-post-deployment.yml # Final verification
|
||||||
|
│ └── 99-rollback.yml # Emergency rollback
|
||||||
|
│
|
||||||
|
├── roles/ # ⭐ Ansible roles
|
||||||
|
│ ├── nextcloud_stack/
|
||||||
|
│ │ └── templates/
|
||||||
|
│ │ ├── docker-compose.yml.j2 # Complete stack definition
|
||||||
|
│ │ └── env.j2 # Environment variables
|
||||||
|
│ └── caddy/
|
||||||
|
│ └── templates/
|
||||||
|
│ └── Caddyfile.j2 # Reverse proxy config
|
||||||
|
│
|
||||||
|
└── docs/ # ⭐ Documentation
|
||||||
|
├── README.md # Project overview
|
||||||
|
├── DEPLOYMENT_GUIDE.md # Quick start guide
|
||||||
|
├── TROUBLESHOOTING.md # Common issues & solutions
|
||||||
|
└── BACKUP_RESTORE.md # Backup procedures
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stack Components (12 Services)
|
||||||
|
|
||||||
|
**Public Services** (via HTTPS):
|
||||||
|
- Nextcloud (file sync & collaboration)
|
||||||
|
- OnlyOffice (document editor)
|
||||||
|
- Excalidraw (whiteboard)
|
||||||
|
- Obsidian (note-taking)
|
||||||
|
|
||||||
|
**Infrastructure**:
|
||||||
|
- PostgreSQL 18 (database)
|
||||||
|
- Redis 7 (cache)
|
||||||
|
- Caddy (reverse proxy + automatic SSL)
|
||||||
|
|
||||||
|
**Management** (Tailscale-only):
|
||||||
|
- Homarr (dashboard)
|
||||||
|
- Dockhand (container manager)
|
||||||
|
- Uptime Kuma (monitoring)
|
||||||
|
|
||||||
|
**Automation**:
|
||||||
|
- Watchtower (auto-updates with safety exclusions)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Configure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive wizard collects all configuration:
|
||||||
|
- Server details (IP, domain, SSH)
|
||||||
|
- User information
|
||||||
|
- Admin credentials
|
||||||
|
- Subdomain preferences
|
||||||
|
- Tailscale auth key (optional)
|
||||||
|
- Monitoring settings
|
||||||
|
|
||||||
|
**Generates:**
|
||||||
|
- `inventory/hosts.yml` - Server inventory
|
||||||
|
- `inventory/group_vars/all/vars.yml` - Public config
|
||||||
|
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
|
||||||
|
|
||||||
|
### 2. Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Duration:** 30-45 minutes per server
|
||||||
|
|
||||||
|
### 3. Access
|
||||||
|
|
||||||
|
- Nextcloud: `https://cloud.yourdomain.com`
|
||||||
|
- Homarr: `https://home.yourdomain.com` (Tailscale)
|
||||||
|
- Uptime Kuma: `https://uptime.yourdomain.com` (Tailscale)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Key Features
|
||||||
|
|
||||||
|
### 🔒 Security
|
||||||
|
|
||||||
|
- **Firewall**: UFW configured (only SSH, HTTP, HTTPS, Tailscale open)
|
||||||
|
- **Fail2ban**: SSH brute-force protection (5 failures = 10 min ban)
|
||||||
|
- **Automatic Security Updates**: Unattended upgrades enabled
|
||||||
|
- **Secret Management**: Ansible Vault encryption for all credentials
|
||||||
|
- **Access Control**: Management UIs restricted to Tailscale network
|
||||||
|
- **SSL/TLS**: Automatic Let's Encrypt certificates via Caddy
|
||||||
|
- **Container Isolation**: Dedicated Docker network
|
||||||
|
|
||||||
|
### 💾 Backups
|
||||||
|
|
||||||
|
- **Daily**: PostgreSQL database dumps (3:00 AM)
|
||||||
|
- **Weekly**: Full volume backups (Sundays, 3:00 AM)
|
||||||
|
- **Retention**: 30 days
|
||||||
|
- **Location**: `/opt/nextcloud-stack/backups/`
|
||||||
|
- **Off-site Ready**: rclone pre-installed
|
||||||
|
- **Automated**: Cron jobs configured
|
||||||
|
- **Tested**: Documented restore procedures
|
||||||
|
|
||||||
|
### 🔄 Updates
|
||||||
|
|
||||||
|
**Automated (Watchtower)**:
|
||||||
|
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
|
||||||
|
|
||||||
|
**Monitor Only**:
|
||||||
|
- OnlyOffice (notifications, manual update)
|
||||||
|
|
||||||
|
**Manual Required**:
|
||||||
|
- Nextcloud, PostgreSQL, Redis (too critical to auto-update)
|
||||||
|
|
||||||
|
### 🌐 Multi-Server Support
|
||||||
|
|
||||||
|
Deploy identical stacks to multiple servers:
|
||||||
|
- Each server gets unique domain
|
||||||
|
- Fully independent deployments
|
||||||
|
- Shared Tailscale network (optional)
|
||||||
|
|
||||||
|
### 📊 Monitoring
|
||||||
|
|
||||||
|
- **Uptime Kuma**: Service health monitoring
|
||||||
|
- **Email Alerts**: Configurable notifications
|
||||||
|
- **Status Page**: Optional public status page
|
||||||
|
- **Container Health**: Docker healthchecks
|
||||||
|
- **SSL Monitoring**: Certificate expiry tracking
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
| Document | Purpose |
|
||||||
|
|----------|---------|
|
||||||
|
| [README.md](README.md) | Complete project overview, features, usage |
|
||||||
|
| [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) | Step-by-step deployment instructions |
|
||||||
|
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Common issues and solutions |
|
||||||
|
| [BACKUP_RESTORE.md](BACKUP_RESTORE.md) | Backup and restore procedures |
|
||||||
|
| [AGENTS.md](AGENTS.md) | Docker Compose & Ansible guidelines |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Makefile Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make help # Show all commands
|
||||||
|
make setup # Run interactive setup
|
||||||
|
make ping # Test connectivity
|
||||||
|
make check # Run preflight checks
|
||||||
|
make deploy # Full deployment
|
||||||
|
make deploy-dry # Test run (no changes)
|
||||||
|
make status # Show container status
|
||||||
|
make logs # View all logs
|
||||||
|
make backup # Run manual backup
|
||||||
|
make restart # Restart all containers
|
||||||
|
make edit-vault # Edit encrypted secrets
|
||||||
|
make rekey-vault # Change vault password
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Design Principles
|
||||||
|
|
||||||
|
### 1. **User-Friendly**
|
||||||
|
- Interactive setup script (no manual configuration editing)
|
||||||
|
- Automatic SSL certificates
|
||||||
|
- Clear documentation
|
||||||
|
- Helpful error messages
|
||||||
|
|
||||||
|
### 2. **Secure by Default**
|
||||||
|
- Minimal firewall rules
|
||||||
|
- Encrypted secrets (Ansible Vault)
|
||||||
|
- Tailscale-only admin interfaces
|
||||||
|
- Automatic security updates
|
||||||
|
- No hardcoded credentials
|
||||||
|
|
||||||
|
### 3. **Production-Ready**
|
||||||
|
- Automated backups
|
||||||
|
- Monitoring included
|
||||||
|
- Health checks configured
|
||||||
|
- Documented restore procedures
|
||||||
|
- Emergency rollback playbook
|
||||||
|
|
||||||
|
### 4. **Maintainable**
|
||||||
|
- Modular playbook structure
|
||||||
|
- Clear variable naming
|
||||||
|
- Comprehensive comments
|
||||||
|
- Role-based organization
|
||||||
|
- Git-friendly (.gitignore configured)
|
||||||
|
|
||||||
|
### 5. **Safe Updates**
|
||||||
|
- Critical services excluded from auto-update
|
||||||
|
- Watchtower uses label-based control
|
||||||
|
- Manual update procedures documented
|
||||||
|
- Rollback capability included
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Deployment Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 1. SETUP (./setup.sh) │
|
||||||
|
│ • Collect server details, domains, credentials │
|
||||||
|
│ • Generate encrypted inventory & variables │
|
||||||
|
│ • Create Ansible Vault with secrets │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 2. PREFLIGHT CHECKS (01-preflight-checks.yml) │
|
||||||
|
│ • Verify Ansible version, SSH connectivity │
|
||||||
|
│ • Check disk space, ports availability │
|
||||||
|
│ • Validate DNS records, detect LXC │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 3. SYSTEM SETUP (02-system-setup.yml) │
|
||||||
|
│ • Update packages, install essentials │
|
||||||
|
│ • Configure UFW firewall, fail2ban │
|
||||||
|
│ • Enable automatic security updates │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 4. DOCKER SETUP (03-docker-setup.yml) │
|
||||||
|
│ • Remove old Docker installations │
|
||||||
|
│ • Install Docker CE + Compose v2 │
|
||||||
|
│ • Configure Docker daemon │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 5. TAILSCALE SETUP (04-tailscale-setup.yml) │
|
||||||
|
│ • Install Tailscale │
|
||||||
|
│ • Activate if auth key provided │
|
||||||
|
│ • Enable systemd service │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 6. DEPLOY STACK (05-deploy-stack.yml) │
|
||||||
|
│ • Create directory structure │
|
||||||
|
│ • Template docker-compose.yml, .env, configs │
|
||||||
|
│ • Pull Docker images, start containers │
|
||||||
|
│ • Wait for services healthy │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 7. CONFIGURE CADDY (06-configure-caddy.yml) │
|
||||||
|
│ • Validate Caddyfile syntax │
|
||||||
|
│ • Reload Caddy configuration │
|
||||||
|
│ • Obtain Let's Encrypt SSL certificates │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 8. SETUP BACKUPS (07-setup-backups.yml) │
|
||||||
|
│ • Create backup script │
|
||||||
|
│ • Configure cron jobs (daily/weekly) │
|
||||||
|
│ • Run test backup │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 9. POST-DEPLOYMENT (08-post-deployment.yml) │
|
||||||
|
│ • Verify all containers running │
|
||||||
|
│ • Install Nextcloud apps │
|
||||||
|
│ • Configure background jobs │
|
||||||
|
│ • Create deployment report │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
↓
|
||||||
|
✅ DEPLOYMENT COMPLETE!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Template Files
|
||||||
|
|
||||||
|
### docker-compose.yml.j2
|
||||||
|
|
||||||
|
Complete Docker Compose stack with:
|
||||||
|
- All 12 services configured
|
||||||
|
- Health checks for critical services
|
||||||
|
- Proper dependencies (depends_on with conditions)
|
||||||
|
- Watchtower labels for update control
|
||||||
|
- Named volumes and networks
|
||||||
|
- Environment variables from .env
|
||||||
|
|
||||||
|
**Location**: `roles/nextcloud_stack/templates/docker-compose.yml.j2`
|
||||||
|
|
||||||
|
### Caddyfile.j2
|
||||||
|
|
||||||
|
Caddy reverse proxy configuration with:
|
||||||
|
- Automatic SSL for all domains
|
||||||
|
- Public services (Nextcloud, OnlyOffice, etc.)
|
||||||
|
- Tailscale-restricted services (Homarr, Dockhand, Uptime Kuma)
|
||||||
|
- Security headers
|
||||||
|
- Large file upload support (10GB)
|
||||||
|
- Optional public status page
|
||||||
|
|
||||||
|
**Location**: `roles/caddy/templates/Caddyfile.j2`
|
||||||
|
|
||||||
|
### env.j2
|
||||||
|
|
||||||
|
Environment variables template with:
|
||||||
|
- Database credentials
|
||||||
|
- Redis password
|
||||||
|
- Nextcloud admin credentials
|
||||||
|
- Application secrets (Homarr)
|
||||||
|
- Domain configuration
|
||||||
|
- Timezone settings
|
||||||
|
|
||||||
|
**Location**: `roles/nextcloud_stack/templates/env.j2`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Considerations
|
||||||
|
|
||||||
|
### Secrets Management
|
||||||
|
|
||||||
|
All sensitive data encrypted with Ansible Vault:
|
||||||
|
- Database passwords
|
||||||
|
- Admin credentials
|
||||||
|
- Application secrets
|
||||||
|
- Tailscale auth keys
|
||||||
|
|
||||||
|
**Never committed in plain text!**
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
|
||||||
|
- **Public**: Only Nextcloud and productivity apps
|
||||||
|
- **Tailscale-only**: All management interfaces
|
||||||
|
- **Internal**: Database and Redis (no external access)
|
||||||
|
|
||||||
|
### Firewall Rules (UFW)
|
||||||
|
|
||||||
|
```
|
||||||
|
22/tcp - SSH
|
||||||
|
80/tcp - HTTP (redirects to HTTPS)
|
||||||
|
443/tcp - HTTPS
|
||||||
|
443/udp - HTTP/3 (QUIC)
|
||||||
|
41641/udp - Tailscale
|
||||||
|
Default: DENY
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Strategy
|
||||||
|
|
||||||
|
**Why not auto-update everything?**
|
||||||
|
|
||||||
|
- **Nextcloud**: Database migrations may fail automatically
|
||||||
|
- **PostgreSQL**: Schema changes could break Nextcloud
|
||||||
|
- **Redis**: Unlikely issues, but excluded for consistency
|
||||||
|
|
||||||
|
**Safe to auto-update:**
|
||||||
|
- Stateless applications (Excalidraw, Obsidian)
|
||||||
|
- Infrastructure (Caddy)
|
||||||
|
- Management tools (Homarr, Dockhand)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Resource Usage (Approximate)
|
||||||
|
|
||||||
|
### Disk Space
|
||||||
|
|
||||||
|
- Docker images: ~5GB
|
||||||
|
- Initial volumes: ~2GB
|
||||||
|
- Backups (30 days): ~50GB
|
||||||
|
- **Total recommended**: 100GB minimum
|
||||||
|
|
||||||
|
### Memory
|
||||||
|
|
||||||
|
- PostgreSQL: ~256MB
|
||||||
|
- Redis: ~256MB (configured limit)
|
||||||
|
- Nextcloud: ~512MB
|
||||||
|
- OnlyOffice: ~2GB
|
||||||
|
- Other services: ~1GB combined
|
||||||
|
- **Total**: 4-6GB RAM recommended
|
||||||
|
|
||||||
|
### Network
|
||||||
|
|
||||||
|
- **Inbound**: 80, 443 (HTTP/HTTPS), 41641 (Tailscale)
|
||||||
|
- **Outbound**: Internet access required for Let's Encrypt, Docker images, updates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Next Steps
|
||||||
|
|
||||||
|
### Immediate
|
||||||
|
|
||||||
|
1. **Test the deployment** on a test server first
|
||||||
|
2. **Verify DNS** is configured correctly
|
||||||
|
3. **Run setup.sh** and answer questions carefully
|
||||||
|
4. **Deploy** with `make deploy`
|
||||||
|
5. **Access Nextcloud** and complete initial setup
|
||||||
|
|
||||||
|
### After Deployment
|
||||||
|
|
||||||
|
1. **Configure Uptime Kuma** monitoring
|
||||||
|
2. **Customize Homarr** dashboard
|
||||||
|
3. **Test backups** with `make backup`
|
||||||
|
4. **Enable 2FA** in Nextcloud
|
||||||
|
5. **Install additional Nextcloud apps** as needed
|
||||||
|
|
||||||
|
### Ongoing
|
||||||
|
|
||||||
|
1. **Monitor** service health in Uptime Kuma
|
||||||
|
2. **Review backups** monthly
|
||||||
|
3. **Test restore** procedures quarterly
|
||||||
|
4. **Update Nextcloud** when new versions release (manually)
|
||||||
|
5. **Check logs** for any issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Tips & Best Practices
|
||||||
|
|
||||||
|
### For LXC Containers
|
||||||
|
|
||||||
|
**On LXC host, enable nested virtualization:**
|
||||||
|
```bash
|
||||||
|
lxc config set CONTAINER_NAME security.nesting true
|
||||||
|
lxc restart CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Production Use
|
||||||
|
|
||||||
|
1. Use strong passwords (not example passwords)
|
||||||
|
2. Enable Nextcloud 2FA
|
||||||
|
3. Configure off-site backups with rclone
|
||||||
|
4. Set up Uptime Kuma email alerts
|
||||||
|
5. Review logs regularly
|
||||||
|
|
||||||
|
### For Multiple Servers
|
||||||
|
|
||||||
|
Each server can have:
|
||||||
|
- Different domain
|
||||||
|
- Different subdomain preferences
|
||||||
|
- Same or different Tailscale network
|
||||||
|
|
||||||
|
Just run `setup.sh` and add all servers when prompted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Known Limitations
|
||||||
|
|
||||||
|
1. **Watchtower is archived** (December 2025)
|
||||||
|
- Still works, but no longer maintained
|
||||||
|
- Consider alternative update strategies long-term
|
||||||
|
|
||||||
|
2. **OnlyOffice needs 2GB+ RAM**
|
||||||
|
- May be slow on low-memory servers
|
||||||
|
- Consider disabling if not needed
|
||||||
|
|
||||||
|
3. **First-time deployment takes 30-45 min**
|
||||||
|
- Mostly Docker image downloads
|
||||||
|
- Subsequent deploys are faster
|
||||||
|
|
||||||
|
4. **Let's Encrypt rate limits**
|
||||||
|
- 50 certs/week per domain
|
||||||
|
- Use staging server for testing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
MIT License - Use freely, modify as needed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🙏 Acknowledgments
|
||||||
|
|
||||||
|
Built with these amazing open-source projects:
|
||||||
|
|
||||||
|
- **Nextcloud** - File sync and collaboration
|
||||||
|
- **Caddy** - Automatic HTTPS web server
|
||||||
|
- **Ansible** - IT automation
|
||||||
|
- **Docker** - Containerization
|
||||||
|
- **PostgreSQL** - Database
|
||||||
|
- **Tailscale** - Zero-config VPN
|
||||||
|
- **OnlyOffice**, **Excalidraw**, **Obsidian** - Productivity tools
|
||||||
|
- **Homarr**, **Dockhand**, **Uptime Kuma** - Management & monitoring
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) - Getting started
|
||||||
|
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Fix common issues
|
||||||
|
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup procedures
|
||||||
|
|
||||||
|
**Project Files:**
|
||||||
|
All source code, playbooks, and templates included in this repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Checklist for New Users
|
||||||
|
|
||||||
|
Before deploying:
|
||||||
|
- [ ] Read DEPLOYMENT_GUIDE.md
|
||||||
|
- [ ] Install Ansible 2.14+ on control machine
|
||||||
|
- [ ] Have Ubuntu 20.04+ server ready
|
||||||
|
- [ ] Configure DNS A records
|
||||||
|
- [ ] Test SSH access to server
|
||||||
|
- [ ] Have domain name ready
|
||||||
|
- [ ] Decide on subdomain preferences
|
||||||
|
|
||||||
|
After deploying:
|
||||||
|
- [ ] Access Nextcloud web UI
|
||||||
|
- [ ] Complete Nextcloud setup wizard
|
||||||
|
- [ ] Setup Uptime Kuma monitoring
|
||||||
|
- [ ] Configure Homarr dashboard
|
||||||
|
- [ ] Test backup with `make backup`
|
||||||
|
- [ ] Enable Nextcloud 2FA
|
||||||
|
- [ ] Review deployment report
|
||||||
|
- [ ] Save Ansible Vault password securely
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Project Status**: ✅ Complete and Ready for Use
|
||||||
|
|
||||||
|
**Last Updated**: 2026-02-16
|
||||||
|
|
||||||
|
**Version**: 1.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Happy self-hosting! 🎉**
|
||||||
382
ansible/README.md
Normal file
382
ansible/README.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# Nextcloud Stack - Automated Deployment with Ansible
|
||||||
|
|
||||||
|
Complete automation for deploying a self-hosted Nextcloud productivity stack on Ubuntu-based LXC/VPS servers.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **12 Services**: Nextcloud, OnlyOffice, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma, PostgreSQL, Redis, Caddy, Watchtower
|
||||||
|
- **Automatic SSL**: Let's Encrypt certificates via Caddy
|
||||||
|
- **Secure Management**: Tailscale-only access for admin interfaces
|
||||||
|
- **Auto-updates**: Watchtower with safety exclusions for critical services
|
||||||
|
- **Automated Backups**: Daily database backups, weekly volume backups (30-day retention)
|
||||||
|
- **Monitoring**: Uptime Kuma with configurable alerts
|
||||||
|
- **Multi-server**: Deploy to multiple VPS servers with unique domains
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. **Control Machine** (your laptop):
|
||||||
|
- Ansible 2.14+ installed
|
||||||
|
- SSH access to target servers
|
||||||
|
|
||||||
|
2. **Target Server(s)**:
|
||||||
|
- Ubuntu 20.04+ (LXC container or VPS)
|
||||||
|
- Root or sudo access
|
||||||
|
- Minimum 100GB disk space
|
||||||
|
- Ports 80, 443 available
|
||||||
|
|
||||||
|
3. **DNS Configuration** (BEFORE deployment):
|
||||||
|
```
|
||||||
|
cloud.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
office.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
draw.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
notes.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
home.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
manage.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
uptime.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. **Clone this repository**:
|
||||||
|
```bash
|
||||||
|
git clone <your-repo-url>
|
||||||
|
cd ansible-nextcloud-deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run interactive setup**:
|
||||||
|
```bash
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will ask for:
|
||||||
|
- Server IP addresses and domains
|
||||||
|
- Your name and email
|
||||||
|
- Admin credentials
|
||||||
|
- Subdomain preferences
|
||||||
|
- Tailscale auth key (optional)
|
||||||
|
- Monitoring email
|
||||||
|
- Ansible Vault password
|
||||||
|
|
||||||
|
3. **Deploy the stack**:
|
||||||
|
```bash
|
||||||
|
make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Access your services**:
|
||||||
|
- Nextcloud: `https://cloud.yourdomain.com`
|
||||||
|
- OnlyOffice: `https://office.yourdomain.com`
|
||||||
|
- Homarr (Tailscale only): `https://home.yourdomain.com`
|
||||||
|
- Uptime Kuma (Tailscale only): `https://uptime.yourdomain.com`
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ansible-nextcloud-deployment/
|
||||||
|
├── setup.sh # Interactive configuration script
|
||||||
|
├── Makefile # Convenient command shortcuts
|
||||||
|
├── ansible.cfg # Ansible configuration
|
||||||
|
├── inventory/
|
||||||
|
│ ├── hosts.yml # Generated by setup.sh
|
||||||
|
│ └── group_vars/all/
|
||||||
|
│ ├── vars.yml # Public variables
|
||||||
|
│ └── vault.yml # Encrypted secrets
|
||||||
|
├── playbooks/
|
||||||
|
│ ├── site.yml # Main orchestrator
|
||||||
|
│ ├── 01-preflight-checks.yml # Pre-deployment validation
|
||||||
|
│ ├── 02-system-setup.yml # System packages & security
|
||||||
|
│ ├── 03-docker-setup.yml # Docker installation
|
||||||
|
│ ├── 04-tailscale-setup.yml # VPN setup
|
||||||
|
│ ├── 05-deploy-stack.yml # Docker Compose deployment
|
||||||
|
│ ├── 06-configure-caddy.yml # Reverse proxy configuration
|
||||||
|
│ ├── 07-setup-backups.yml # Backup automation
|
||||||
|
│ └── 08-post-deployment.yml # Final verification
|
||||||
|
└── roles/
|
||||||
|
├── nextcloud_stack/
|
||||||
|
│ └── templates/
|
||||||
|
│ ├── docker-compose.yml.j2
|
||||||
|
│ └── env.j2
|
||||||
|
└── caddy/
|
||||||
|
└── templates/
|
||||||
|
└── Caddyfile.j2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Makefile Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make help # Show all commands
|
||||||
|
make setup # Run interactive setup
|
||||||
|
make ping # Test connectivity
|
||||||
|
make check # Run preflight checks
|
||||||
|
make deploy # Full deployment
|
||||||
|
make deploy-dry # Dry run (no changes)
|
||||||
|
make status # Show container status
|
||||||
|
make logs # View container logs
|
||||||
|
make backup # Run manual backup
|
||||||
|
make restart # Restart all containers
|
||||||
|
make edit-vault # Edit encrypted secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Stack
|
||||||
|
|
||||||
|
| Service | Purpose | Port | Access | Auto-Update |
|
||||||
|
|---------|---------|------|--------|-------------|
|
||||||
|
| Caddy | Reverse proxy + SSL | 80, 443 | Public | ✅ |
|
||||||
|
| Nextcloud | File sync & collaboration | Internal | Public (via Caddy) | ❌ Manual |
|
||||||
|
| PostgreSQL 18 | Database | Internal | Internal only | ❌ Manual |
|
||||||
|
| Redis 7 | Cache layer | Internal | Internal only | ❌ Manual |
|
||||||
|
| OnlyOffice | Document editor | Internal | Public (via Caddy) | 👁️ Monitor |
|
||||||
|
| Excalidraw | Whiteboard tool | Internal | Public (via Caddy) | ✅ |
|
||||||
|
| Obsidian | Note-taking | Internal | Public (via Caddy) | ✅ |
|
||||||
|
| Homarr | Dashboard | Internal | Tailscale only | ✅ |
|
||||||
|
| Dockhand | Container manager | 3003 | Tailscale only | ✅ |
|
||||||
|
| Uptime Kuma | Monitoring | Internal | Tailscale only | ✅ |
|
||||||
|
| Watchtower | Auto-updater | N/A | N/A | N/A |
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
|
||||||
|
- **Firewall**: UFW configured with minimal open ports
|
||||||
|
- **Fail2ban**: SSH brute-force protection
|
||||||
|
- **Automatic Updates**: Unattended security updates enabled
|
||||||
|
- **Secret Management**: Ansible Vault encryption for all credentials
|
||||||
|
- **Access Control**: Management UIs restricted to Tailscale network
|
||||||
|
- **SSL/TLS**: Automatic Let's Encrypt certificates
|
||||||
|
- **Container Isolation**: Dedicated Docker network
|
||||||
|
|
||||||
|
## Backup System
|
||||||
|
|
||||||
|
- **Daily**: PostgreSQL database dumps (3:00 AM)
|
||||||
|
- **Weekly**: Full volume backups (Sundays)
|
||||||
|
- **Retention**: 30 days
|
||||||
|
- **Location**: `/opt/nextcloud-stack/backups/`
|
||||||
|
- **rclone**: Pre-installed for future remote backups
|
||||||
|
|
||||||
|
### Manual Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
./backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore from Backup
|
||||||
|
|
||||||
|
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for detailed procedures.
|
||||||
|
|
||||||
|
## Tailscale Setup
|
||||||
|
|
||||||
|
If you didn't provide an auth key during setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
sudo tailscale up
|
||||||
|
```
|
||||||
|
|
||||||
|
Then access management interfaces via Tailscale IP or MagicDNS.
|
||||||
|
|
||||||
|
## Updating Services
|
||||||
|
|
||||||
|
### Safe to Auto-Update (Watchtower handles)
|
||||||
|
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
|
||||||
|
|
||||||
|
### Monitor Only (notifications, no auto-update)
|
||||||
|
- OnlyOffice
|
||||||
|
|
||||||
|
### Manual Update Required
|
||||||
|
- Nextcloud
|
||||||
|
- PostgreSQL
|
||||||
|
- Redis
|
||||||
|
|
||||||
|
### Updating Nextcloud Manually
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
|
||||||
|
# 1. Backup
|
||||||
|
./backup.sh
|
||||||
|
|
||||||
|
# 2. Enable maintenance mode
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --on
|
||||||
|
|
||||||
|
# 3. Update
|
||||||
|
docker compose pull next
|
||||||
|
docker compose up -d next
|
||||||
|
|
||||||
|
# 4. Run database migrations
|
||||||
|
docker exec -u www-data next php occ upgrade
|
||||||
|
|
||||||
|
# 5. Disable maintenance mode
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### DNS Not Configured
|
||||||
|
**Error**: Let's Encrypt fails to issue certificates
|
||||||
|
|
||||||
|
**Solution**: Ensure all DNS A records point to your server IP. Check with:
|
||||||
|
```bash
|
||||||
|
dig +short cloud.yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### LXC Container Issues
|
||||||
|
**Error**: Docker fails to start
|
||||||
|
|
||||||
|
**Solution**: On LXC host, enable nested virtualization:
|
||||||
|
```bash
|
||||||
|
lxc config set CONTAINER_NAME security.nesting true
|
||||||
|
lxc restart CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
**Error**: Port 80 or 443 already bound
|
||||||
|
|
||||||
|
**Solution**: Check what's using the ports:
|
||||||
|
```bash
|
||||||
|
sudo ss -tlnp | grep ':80\|:443'
|
||||||
|
sudo systemctl stop apache2 # or nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nextcloud Stuck in Maintenance Mode
|
||||||
|
```bash
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose logs -f [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Deployment Tasks
|
||||||
|
|
||||||
|
1. **Login to Nextcloud**:
|
||||||
|
- URL: `https://cloud.yourdomain.com`
|
||||||
|
- Username: (set during setup)
|
||||||
|
- Password: (stored in vault)
|
||||||
|
|
||||||
|
2. **Setup Uptime Kuma**:
|
||||||
|
- URL: `https://uptime.yourdomain.com` (via Tailscale)
|
||||||
|
- Create admin account on first visit
|
||||||
|
- Configure monitors for your services
|
||||||
|
|
||||||
|
3. **Configure Homarr Dashboard**:
|
||||||
|
- URL: `https://home.yourdomain.com` (via Tailscale)
|
||||||
|
- Add service tiles
|
||||||
|
- Customize layout
|
||||||
|
|
||||||
|
4. **Configure OnlyOffice in Nextcloud**:
|
||||||
|
- Nextcloud Settings → Administration → Office
|
||||||
|
- Document Editing Service: `https://office.yourdomain.com`
|
||||||
|
|
||||||
|
5. **Test Backups**:
|
||||||
|
```bash
|
||||||
|
make backup
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Locations on Server
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/nextcloud-stack/
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── .env
|
||||||
|
├── backup.sh
|
||||||
|
├── configs/
|
||||||
|
│ ├── caddy/Caddyfile
|
||||||
|
│ └── nextcloud/
|
||||||
|
├── data/
|
||||||
|
│ ├── homarr/
|
||||||
|
│ └── obsidian/
|
||||||
|
└── backups/
|
||||||
|
├── database/
|
||||||
|
└── volumes/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
All secrets are stored in `inventory/group_vars/all/vault.yml` (encrypted).
|
||||||
|
|
||||||
|
To edit:
|
||||||
|
```bash
|
||||||
|
make edit-vault
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple Server Deployment
|
||||||
|
|
||||||
|
The setup script supports multiple servers. Each can have its own domain:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Server 1: 192.168.1.100 → cloud1.example.com
|
||||||
|
Server 2: 192.168.1.101 → cloud2.anotherdomain.net
|
||||||
|
Server 3: 192.168.1.102 → personal.mydomain.org
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy to all:
|
||||||
|
```bash
|
||||||
|
make deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy to specific server:
|
||||||
|
```bash
|
||||||
|
ansible-playbook playbooks/site.yml --limit server_hostname --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Check Container Status
|
||||||
|
```bash
|
||||||
|
make status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart Services
|
||||||
|
```bash
|
||||||
|
make restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Images (safe services only)
|
||||||
|
```bash
|
||||||
|
make update
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
make logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstallation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down -v # WARNING: Deletes all data!
|
||||||
|
cd /opt
|
||||||
|
rm -rf nextcloud-stack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for common issues
|
||||||
|
- See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for backup procedures
|
||||||
|
- See [AGENTS.md](AGENTS.md) for development guidelines
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
- Nextcloud team
|
||||||
|
- Caddy web server
|
||||||
|
- Ansible community
|
||||||
|
- All open-source contributors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Built with ❤️ for self-hosting enthusiasts**
|
||||||
192
ansible/START_HERE.md
Normal file
192
ansible/START_HERE.md
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# 👋 Welcome to Nextcloud Stack Ansible Deployment!
|
||||||
|
|
||||||
|
## 🚀 Quick Start (Choose Your Path)
|
||||||
|
|
||||||
|
### 🏃 I Want to Deploy NOW
|
||||||
|
1. Read → [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) (10 min read)
|
||||||
|
2. Run → `./setup.sh` (5-10 min)
|
||||||
|
3. Deploy → `make deploy` (30-45 min)
|
||||||
|
|
||||||
|
### 📚 I Want to Learn More First
|
||||||
|
1. Read → [README.md](README.md) - Complete project overview
|
||||||
|
2. Read → [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) - Design & architecture
|
||||||
|
3. Then → Follow Quick Start above
|
||||||
|
|
||||||
|
### 🔧 I'm Having Issues
|
||||||
|
1. Check → [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
2. Check logs → `make logs`
|
||||||
|
3. Review → [BACKUP_RESTORE.md](BACKUP_RESTORE.md) if you need to restore
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Documentation Index
|
||||||
|
|
||||||
|
| Document | Purpose | Read Time |
|
||||||
|
|----------|---------|-----------|
|
||||||
|
| **[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)** | Step-by-step deployment instructions | 10 min |
|
||||||
|
| **[README.md](README.md)** | Complete project overview & features | 15 min |
|
||||||
|
| **[PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)** | Technical details & design | 10 min |
|
||||||
|
| **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** | Common issues & solutions | Reference |
|
||||||
|
| **[BACKUP_RESTORE.md](BACKUP_RESTORE.md)** | Backup & restore procedures | Reference |
|
||||||
|
| **[AGENTS.md](AGENTS.md)** | Docker & Ansible development guidelines | Reference |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Pre-Deployment Checklist
|
||||||
|
|
||||||
|
Before you begin, ensure you have:
|
||||||
|
|
||||||
|
- [ ] Ubuntu 20.04+ server (LXC or VPS)
|
||||||
|
- [ ] Root or sudo access to server
|
||||||
|
- [ ] Minimum 100GB disk space on server
|
||||||
|
- [ ] Ansible 2.14+ installed on your laptop/control machine
|
||||||
|
- [ ] Domain name with DNS control
|
||||||
|
- [ ] SSH access configured to server
|
||||||
|
|
||||||
|
**Critical:** DNS A records must point to your server BEFORE deployment!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 What You'll Get
|
||||||
|
|
||||||
|
After deployment, you'll have:
|
||||||
|
|
||||||
|
### Public Services (HTTPS)
|
||||||
|
- **Nextcloud** - File sync & collaboration
|
||||||
|
- **OnlyOffice** - Document editor (Word, Excel, PowerPoint)
|
||||||
|
- **Excalidraw** - Whiteboard & diagrams
|
||||||
|
- **Obsidian** - Note-taking & knowledge management
|
||||||
|
|
||||||
|
### Management Tools (Tailscale-only, secure)
|
||||||
|
- **Homarr** - Beautiful dashboard for all services
|
||||||
|
- **Dockhand** - Container management interface
|
||||||
|
- **Uptime Kuma** - Monitoring & alerts
|
||||||
|
|
||||||
|
### Infrastructure (automatic, invisible)
|
||||||
|
- **Caddy** - Reverse proxy with automatic SSL
|
||||||
|
- **PostgreSQL** - Database
|
||||||
|
- **Redis** - Cache for better performance
|
||||||
|
- **Watchtower** - Automatic updates (safe services only)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Key Features
|
||||||
|
|
||||||
|
- ✅ **One-command deployment** - `make deploy` does everything
|
||||||
|
- ✅ **Automatic SSL** - Let's Encrypt certificates via Caddy
|
||||||
|
- ✅ **Secure by default** - Firewall, fail2ban, Vault encryption
|
||||||
|
- ✅ **Auto backups** - Daily DB, weekly volumes (30-day retention)
|
||||||
|
- ✅ **Multi-server** - Deploy to multiple VPS with unique domains
|
||||||
|
- ✅ **Production-ready** - Monitoring, backups, documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/liph/programming/ansible/
|
||||||
|
├── setup.sh ⭐ # Start here! Interactive setup
|
||||||
|
├── Makefile # Convenient commands (make deploy)
|
||||||
|
├── playbooks/ # 10 Ansible playbooks
|
||||||
|
│ ├── site.yml # Main orchestrator
|
||||||
|
│ ├── 01-preflight-checks.yml # Validates environment
|
||||||
|
│ ├── 02-system-setup.yml # Packages & security
|
||||||
|
│ ├── 03-docker-setup.yml # Docker installation
|
||||||
|
│ ├── 04-tailscale-setup.yml # VPN setup
|
||||||
|
│ ├── 05-deploy-stack.yml # Main deployment
|
||||||
|
│ ├── 06-configure-caddy.yml # SSL & reverse proxy
|
||||||
|
│ ├── 07-setup-backups.yml # Backup automation
|
||||||
|
│ ├── 08-post-deployment.yml # Verification
|
||||||
|
│ └── 99-rollback.yml # Emergency rollback
|
||||||
|
├── roles/ # Templates & configs
|
||||||
|
│ ├── nextcloud_stack/
|
||||||
|
│ │ └── templates/
|
||||||
|
│ │ ├── docker-compose.yml.j2 # 12-service stack
|
||||||
|
│ │ └── env.j2 # Environment vars
|
||||||
|
│ └── caddy/
|
||||||
|
│ └── templates/
|
||||||
|
│ └── Caddyfile.j2 # Reverse proxy config
|
||||||
|
└── docs/ # You are here!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Common Commands
|
||||||
|
|
||||||
|
After setup, you'll use these frequently:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make deploy # Deploy everything
|
||||||
|
make status # Check container status
|
||||||
|
make logs # View all logs
|
||||||
|
make backup # Run manual backup
|
||||||
|
make restart # Restart all containers
|
||||||
|
make edit-vault # Edit encrypted secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Need Help?
|
||||||
|
|
||||||
|
1. **Common Issues**: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
2. **Backup/Restore**: [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
|
||||||
|
3. **Full Documentation**: [README.md](README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 30-Second Overview
|
||||||
|
|
||||||
|
This project automates deploying a complete self-hosted productivity stack:
|
||||||
|
|
||||||
|
1. **Configure once**: `./setup.sh` collects all info
|
||||||
|
2. **Deploy anywhere**: `make deploy` to any Ubuntu server
|
||||||
|
3. **Use immediately**: Access via `https://cloud.yourdomain.com`
|
||||||
|
|
||||||
|
**Time to deploy:** 30-45 minutes (mostly automatic)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Recommended First Steps
|
||||||
|
|
||||||
|
### For Complete Beginners
|
||||||
|
1. Read [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
|
||||||
|
2. Configure DNS for your domain
|
||||||
|
3. Run `./setup.sh` on your laptop
|
||||||
|
4. Run `make deploy` and wait
|
||||||
|
5. Access your new Nextcloud!
|
||||||
|
|
||||||
|
### For Experienced Users
|
||||||
|
1. Skim [README.md](README.md) to understand the stack
|
||||||
|
2. Review [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) for architecture
|
||||||
|
3. Run `./setup.sh` and `make deploy`
|
||||||
|
4. Review generated configs in `/opt/nextcloud-stack/`
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
1. Read [AGENTS.md](AGENTS.md) for development guidelines
|
||||||
|
2. Review playbooks in `playbooks/` directory
|
||||||
|
3. Check templates in `roles/*/templates/`
|
||||||
|
4. Customize as needed for your use case
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌟 What Makes This Special?
|
||||||
|
|
||||||
|
- **User-friendly**: Interactive setup, no manual config editing
|
||||||
|
- **Secure**: Ansible Vault, firewalls, Tailscale, automatic updates
|
||||||
|
- **Complete**: Monitoring, backups, SSL, documentation included
|
||||||
|
- **Production-ready**: Tested deployment procedures, rollback capability
|
||||||
|
- **Maintainable**: Clean code, comprehensive docs, modular design
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚦 Ready to Begin?
|
||||||
|
|
||||||
|
**New users**: Start with [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
|
||||||
|
|
||||||
|
**Quick reference**: See [README.md](README.md)
|
||||||
|
|
||||||
|
**Technical details**: Check [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Let's build your self-hosted cloud! 🚀**
|
||||||
509
ansible/TROUBLESHOOTING.md
Normal file
509
ansible/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
# Troubleshooting Guide
|
||||||
|
|
||||||
|
Common issues and solutions for Nextcloud Stack deployment.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [DNS Issues](#dns-issues)
|
||||||
|
- [SSL Certificate Problems](#ssl-certificate-problems)
|
||||||
|
- [Docker Issues](#docker-issues)
|
||||||
|
- [LXC Container Issues](#lxc-container-issues)
|
||||||
|
- [Nextcloud Issues](#nextcloud-issues)
|
||||||
|
- [Database Connection Issues](#database-connection-issues)
|
||||||
|
- [Tailscale Issues](#tailscale-issues)
|
||||||
|
- [Port Conflicts](#port-conflicts)
|
||||||
|
- [Permission Issues](#permission-issues)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DNS Issues
|
||||||
|
|
||||||
|
### Problem: DNS records not resolving
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Let's Encrypt fails to issue certificates
|
||||||
|
- Caddy shows certificate errors
|
||||||
|
- Services inaccessible via domain
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
dig +short cloud.yourdomain.com @8.8.8.8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Ensure all required A records point to your server IP
|
||||||
|
2. Wait for DNS propagation (up to 48 hours, usually minutes)
|
||||||
|
3. Use [DNSChecker.org](https://dnschecker.org) to verify global propagation
|
||||||
|
|
||||||
|
**Required DNS Records:**
|
||||||
|
```
|
||||||
|
cloud.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
office.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
draw.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
notes.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
home.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
manage.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
uptime.yourdomain.com → YOUR_SERVER_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Temporary Workaround:**
|
||||||
|
Edit `/etc/hosts` on your local machine:
|
||||||
|
```
|
||||||
|
YOUR_SERVER_IP cloud.yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SSL Certificate Problems
|
||||||
|
|
||||||
|
### Problem: Let's Encrypt rate limit exceeded
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Error: "too many certificates already issued"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Use Let's Encrypt staging server for testing
|
||||||
|
2. Edit Caddyfile (add to global options):
|
||||||
|
```caddy
|
||||||
|
{
|
||||||
|
email {{ user_email }}
|
||||||
|
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Reload Caddy: `docker exec caddy caddy reload`
|
||||||
|
4. After testing, remove staging server line
|
||||||
|
|
||||||
|
**Rate Limits:**
|
||||||
|
- 50 certificates per domain per week
|
||||||
|
- 5 duplicate certificates per week
|
||||||
|
|
||||||
|
### Problem: Certificate validation failed
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- "Failed to verify" errors in Caddy logs
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
docker logs caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common Causes:**
|
||||||
|
1. DNS not pointing to server
|
||||||
|
2. Firewall blocking port 80/443
|
||||||
|
3. Another service using port 80/443
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Check firewall
|
||||||
|
sudo ufw status
|
||||||
|
|
||||||
|
# Check port usage
|
||||||
|
sudo ss -tlnp | grep ':80\|:443'
|
||||||
|
|
||||||
|
# Check DNS
|
||||||
|
dig +short yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Issues
|
||||||
|
|
||||||
|
### Problem: Docker daemon won't start
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- `docker ps` fails
|
||||||
|
- Error: "Cannot connect to Docker daemon"
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
sudo systemctl status docker
|
||||||
|
sudo journalctl -xu docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Containers keep restarting
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose logs [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common Causes:**
|
||||||
|
1. Configuration errors
|
||||||
|
2. Port conflicts
|
||||||
|
3. Missing dependencies
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Check specific container
|
||||||
|
docker logs next-db
|
||||||
|
docker logs next
|
||||||
|
docker logs caddy
|
||||||
|
|
||||||
|
# Restart specific service
|
||||||
|
docker compose restart next
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LXC Container Issues
|
||||||
|
|
||||||
|
### Problem: Docker fails to start in LXC
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Error: "cgroups: cgroup mountpoint does not exist"
|
||||||
|
- Docker daemon fails to start
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
systemd-detect-virt # Should show "lxc"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution on LXC Host:**
|
||||||
|
```bash
|
||||||
|
# Set security nesting
|
||||||
|
lxc config set CONTAINER_NAME security.nesting true
|
||||||
|
|
||||||
|
# May also need privileged mode
|
||||||
|
lxc config set CONTAINER_NAME security.privileged true
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
lxc restart CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inside LXC Container:**
|
||||||
|
```bash
|
||||||
|
# Verify cgroups
|
||||||
|
mount | grep cgroup
|
||||||
|
|
||||||
|
# Check Docker status
|
||||||
|
sudo systemctl status docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: AppArmor denials in LXC
|
||||||
|
|
||||||
|
**Solution on LXC Host:**
|
||||||
|
```bash
|
||||||
|
lxc config set CONTAINER_NAME raw.lxc "lxc.apparmor.profile=unconfined"
|
||||||
|
lxc restart CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nextcloud Issues
|
||||||
|
|
||||||
|
### Problem: Nextcloud stuck in maintenance mode
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Web interface shows "System in maintenance mode"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Trusted domain error
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- "Access through untrusted domain" error
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
docker exec -u www-data next php occ config:system:set trusted_domains 1 --value=cloud.yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Redis connection failed
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
docker logs next-redis
|
||||||
|
docker exec next-redis redis-cli ping
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Reconfigure Redis in Nextcloud
|
||||||
|
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
|
||||||
|
docker exec -u www-data next php occ config:system:set redis port --value=6379
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: File uploads fail
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Large files won't upload
|
||||||
|
- Error 413 (Payload Too Large)
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
Already configured in Caddyfile for 10GB uploads. Check:
|
||||||
|
```bash
|
||||||
|
docker exec -u www-data next php occ config:system:get max_upload
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: OnlyOffice integration not working
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Install OnlyOffice app
|
||||||
|
docker exec -u www-data next php occ app:install onlyoffice
|
||||||
|
|
||||||
|
# Configure document server URL
|
||||||
|
docker exec -u www-data next php occ config:app:set onlyoffice DocumentServerUrl --value="https://office.yourdomain.com/"
|
||||||
|
|
||||||
|
# Disable JWT (or configure if needed)
|
||||||
|
docker exec -u www-data next php occ config:app:set onlyoffice jwt_secret --value=""
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database Connection Issues
|
||||||
|
|
||||||
|
### Problem: Nextcloud can't connect to database
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Error: "SQLSTATE[08006]"
|
||||||
|
- Nextcloud shows database error
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check if PostgreSQL is running
|
||||||
|
docker ps | grep next-db
|
||||||
|
|
||||||
|
# Check PostgreSQL logs
|
||||||
|
docker logs next-db
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
docker exec next-db pg_isready -U nextcloud
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Restart database
|
||||||
|
docker compose restart next-db
|
||||||
|
|
||||||
|
# Wait for it to be healthy
|
||||||
|
docker exec next-db pg_isready -U nextcloud
|
||||||
|
|
||||||
|
# Restart Nextcloud
|
||||||
|
docker compose restart next
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Database initialization failed
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- PostgreSQL container keeps restarting
|
||||||
|
- Empty database
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Remove volumes and recreate
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down -v
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ WARNING:** This deletes all data! Only use for fresh installations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tailscale Issues
|
||||||
|
|
||||||
|
### Problem: Can't access Tailscale-only services
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Homarr, Dockhand, Uptime Kuma return 403 Forbidden
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check if Tailscale is running
|
||||||
|
sudo tailscale status
|
||||||
|
|
||||||
|
# Get Tailscale IP
|
||||||
|
tailscale ip -4
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Activate Tailscale (if not done)
|
||||||
|
sudo tailscale up
|
||||||
|
|
||||||
|
# Verify connection
|
||||||
|
tailscale status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access via:**
|
||||||
|
- Tailscale IP: `https://100.64.x.x:PORT`
|
||||||
|
- MagicDNS: `https://hostname.tailnet-name.ts.net`
|
||||||
|
|
||||||
|
### Problem: Tailscale not installed
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Re-run Tailscale playbook
|
||||||
|
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Port Conflicts
|
||||||
|
|
||||||
|
### Problem: Port 80 or 443 already in use
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Error: "bind: address already in use"
|
||||||
|
- Caddy won't start
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
sudo ss -tlnp | grep ':80\|:443'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common Culprits:**
|
||||||
|
- Apache2
|
||||||
|
- Nginx
|
||||||
|
- Another Caddy instance
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Stop conflicting service
|
||||||
|
sudo systemctl stop apache2
|
||||||
|
sudo systemctl disable apache2
|
||||||
|
|
||||||
|
# OR
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
sudo systemctl disable nginx
|
||||||
|
|
||||||
|
# Restart Caddy
|
||||||
|
docker compose restart caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Permission Issues
|
||||||
|
|
||||||
|
### Problem: Permission denied errors in Nextcloud
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Can't upload files
|
||||||
|
- Can't install apps
|
||||||
|
|
||||||
|
**Diagnosis:**
|
||||||
|
```bash
|
||||||
|
# Check file permissions
|
||||||
|
docker exec next ls -la /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Fix permissions (run inside container)
|
||||||
|
docker exec next chown -R www-data:www-data /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Docker socket permission denied
|
||||||
|
|
||||||
|
**Symptoms:**
|
||||||
|
- Homarr or Dockhand can't see containers
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
Docker socket is mounted read-only by design for security.
|
||||||
|
This is normal and expected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Emergency Commands
|
||||||
|
|
||||||
|
### Completely restart the stack
|
||||||
|
```bash
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### View all logs in real-time
|
||||||
|
```bash
|
||||||
|
cd /opt/nextcloud-stack
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check container health
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
docker inspect --format='{{.State.Health.Status}}' next
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebuild a specific container
|
||||||
|
```bash
|
||||||
|
docker compose up -d --force-recreate --no-deps next
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emergency backup
|
||||||
|
```bash
|
||||||
|
/opt/nextcloud-stack/backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reset Nextcloud admin password
|
||||||
|
```bash
|
||||||
|
docker exec -u www-data next php occ user:resetpassword admin
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
If none of these solutions work:
|
||||||
|
|
||||||
|
1. **Check logs:**
|
||||||
|
```bash
|
||||||
|
docker compose logs [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check system logs:**
|
||||||
|
```bash
|
||||||
|
sudo journalctl -xe
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Verify configuration:**
|
||||||
|
```bash
|
||||||
|
cat /opt/nextcloud-stack/docker-compose.yml
|
||||||
|
cat /opt/nextcloud-stack/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test connectivity:**
|
||||||
|
```bash
|
||||||
|
curl -I https://cloud.yourdomain.com
|
||||||
|
docker exec caddy caddy validate
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Deployment report:**
|
||||||
|
```bash
|
||||||
|
cat /opt/nextcloud-stack/DEPLOYMENT.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recovery Procedures
|
||||||
|
|
||||||
|
### Restore from backup
|
||||||
|
|
||||||
|
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
|
||||||
|
|
||||||
|
### Complete reinstallation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Backup first!
|
||||||
|
/opt/nextcloud-stack/backup.sh
|
||||||
|
|
||||||
|
# 2. Remove deployment
|
||||||
|
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# 3. Redeploy
|
||||||
|
ansible-playbook playbooks/site.yml --ask-vault-pass
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2026-02-16
|
||||||
35
ansible/ansible.cfg
Normal file
35
ansible/ansible.cfg
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Ansible Configuration File
|
||||||
|
# Generated for Nextcloud Stack Deployment
|
||||||
|
|
||||||
|
[defaults]
|
||||||
|
inventory = inventory/hosts.yml
|
||||||
|
roles_path = roles
|
||||||
|
host_key_checking = False
|
||||||
|
retry_files_enabled = False
|
||||||
|
gathering = smart
|
||||||
|
fact_caching = jsonfile
|
||||||
|
fact_caching_connection = /tmp/ansible_facts
|
||||||
|
fact_caching_timeout = 3600
|
||||||
|
stdout_callback = yaml
|
||||||
|
callbacks_enabled = profile_tasks, timer
|
||||||
|
deprecation_warnings = False
|
||||||
|
|
||||||
|
# SSH settings
|
||||||
|
timeout = 30
|
||||||
|
forks = 5
|
||||||
|
|
||||||
|
# Privilege escalation
|
||||||
|
become = True
|
||||||
|
become_method = sudo
|
||||||
|
become_ask_pass = False
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
become = True
|
||||||
|
become_method = sudo
|
||||||
|
become_user = root
|
||||||
|
become_ask_pass = False
|
||||||
|
|
||||||
|
[ssh_connection]
|
||||||
|
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
|
||||||
|
pipelining = True
|
||||||
|
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
|
||||||
7
ansible/nextcloud/.env
Executable file
7
ansible/nextcloud/.env
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
DB_PASSWORD=
|
||||||
|
DB_USERNAME=
|
||||||
|
DB_DATABASE_NAME=
|
||||||
|
DB_HOST=
|
||||||
|
|
||||||
|
PUID=33
|
||||||
|
PGID=1000
|
||||||
108
ansible/nextcloud/docker-compose.yml
Executable file
108
ansible/nextcloud/docker-compose.yml
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
services:
|
||||||
|
excalidraw:
|
||||||
|
container_name: excalidraw
|
||||||
|
image: docker.io/excalidraw/excalidraw:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3009:80"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- TZ=Europe/Zurich
|
||||||
|
|
||||||
|
# Database (PostgreSQL)
|
||||||
|
next-db:
|
||||||
|
image: docker.io/postgres:18
|
||||||
|
container_name: next-db
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=${DB_DATABASE_NAME}
|
||||||
|
- POSTGRES_USER=${DB_USERNAME}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
-
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
|
||||||
|
# # Redis Cache
|
||||||
|
# next_redis:
|
||||||
|
# image: docker.io/redis:latest
|
||||||
|
# container_name: next-redis
|
||||||
|
# command: redis-server --save 60 1 --loglevel warning
|
||||||
|
# volumes:
|
||||||
|
# - ./data/redis:/data
|
||||||
|
# restart: unless-stopped
|
||||||
|
# networks:
|
||||||
|
# - nextcloud_network
|
||||||
|
|
||||||
|
# Nextcloud Main Application
|
||||||
|
next:
|
||||||
|
image: docker.io/nextcloud:latest
|
||||||
|
container_name: next
|
||||||
|
depends_on:
|
||||||
|
- next-db
|
||||||
|
mem_swappiness: -1
|
||||||
|
ports:
|
||||||
|
- "8808:80"
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=${DB_DATABASE_NAME}
|
||||||
|
- POSTGRES_USER=${DB_USERNAME}
|
||||||
|
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||||
|
- POSTGRES_HOST=${DB_HOST}
|
||||||
|
- NEXTCLOUD_TRUSTED_DOMAINS=next.liphlink.xyz
|
||||||
|
- NEXTCLOUD_ADMIN_USER=liph
|
||||||
|
- NEXTCLOUD_ADMIN_PASSWORD=1ChagenexT
|
||||||
|
volumes:
|
||||||
|
- :/var/www/html
|
||||||
|
- :/var/www/html/config:Z
|
||||||
|
- :/var/www/html/custom_apps
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
|
||||||
|
# OnlyOffice (alternative to Collabora)
|
||||||
|
onlyoffice:
|
||||||
|
image: docker.io/onlyoffice/documentserver:latest
|
||||||
|
container_name: onlyoffice
|
||||||
|
ports:
|
||||||
|
- 8000:80
|
||||||
|
environment:
|
||||||
|
# - JWT_SECRET='S2jaRmOxSxnFoOMhzXotL1QTptDuqhom'
|
||||||
|
- JWT_ENABLED=false
|
||||||
|
- JWT_HEADER=Authorization
|
||||||
|
- JWT_IN_BODY=true
|
||||||
|
volumes:
|
||||||
|
- :/var/www/onlyoffice/Data
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
|
||||||
|
obsidian:
|
||||||
|
image: lscr.io/linuxserver/obsidian:latest
|
||||||
|
container_name: obsidian
|
||||||
|
security_opt:
|
||||||
|
- seccomp:unconfined #optional
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- :/config:z
|
||||||
|
- :/vault:z
|
||||||
|
ports:
|
||||||
|
- 3005:3000
|
||||||
|
- 3004:3001
|
||||||
|
shm_size: "1gb"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pg_data:
|
||||||
|
redis_data:
|
||||||
|
nextcloud_data:
|
||||||
|
nextcloud_config:
|
||||||
|
nextcloud_apps:
|
||||||
|
nextcloud_db_data:
|
||||||
|
onlyoffice_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nextcloud_network:
|
||||||
|
name: nextcloud_network
|
||||||
98
ansible/playbooks/01-preflight-checks.yml
Normal file
98
ansible/playbooks/01-preflight-checks.yml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
# Playbook 01: Preflight Checks
|
||||||
|
# Validates environment before deployment
|
||||||
|
|
||||||
|
- name: Preflight Checks
|
||||||
|
hosts: all
|
||||||
|
gather_facts: yes
|
||||||
|
become: no
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Check Ansible version
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- ansible_version.full is version('2.14', '>=')
|
||||||
|
fail_msg: "Ansible 2.14 or higher is required"
|
||||||
|
success_msg: "Ansible version OK ({{ ansible_version.full }})"
|
||||||
|
delegate_to: localhost
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
- name: Test SSH connectivity
|
||||||
|
ping:
|
||||||
|
|
||||||
|
- name: Check sudo access
|
||||||
|
command: sudo -n true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Check Python3 availability
|
||||||
|
command: python3 --version
|
||||||
|
register: python_version
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display Python version
|
||||||
|
debug:
|
||||||
|
msg: "Python version: {{ python_version.stdout }}"
|
||||||
|
|
||||||
|
- name: Check disk space
|
||||||
|
shell: df -h / | awk 'NR==2 {print $4}'
|
||||||
|
register: disk_space
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Validate sufficient disk space
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- disk_space.stdout is regex('[0-9]+G')
|
||||||
|
fail_msg: "Insufficient disk space. At least 20GB recommended."
|
||||||
|
success_msg: "Disk space OK ({{ disk_space.stdout }} available)"
|
||||||
|
|
||||||
|
- name: Check if ports 80 and 443 are available
|
||||||
|
wait_for:
|
||||||
|
port: "{{ item }}"
|
||||||
|
state: stopped
|
||||||
|
timeout: 1
|
||||||
|
loop:
|
||||||
|
- 80
|
||||||
|
- 443
|
||||||
|
ignore_errors: yes
|
||||||
|
register: port_check
|
||||||
|
|
||||||
|
- name: Detect virtualization type
|
||||||
|
command: systemd-detect-virt
|
||||||
|
register: virt_type
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Warn if running in LXC
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
⚠️ RUNNING IN LXC CONTAINER
|
||||||
|
Docker requires nested virtualization.
|
||||||
|
Ensure on LXC host: lxc config set {{ inventory_hostname }} security.nesting true
|
||||||
|
when: "'lxc' in virt_type.stdout"
|
||||||
|
|
||||||
|
- name: Validate DNS resolution for all subdomains
|
||||||
|
command: dig +short {{ item }}.{{ domain }} @8.8.8.8
|
||||||
|
register: dns_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
loop:
|
||||||
|
- "{{ subdomain_nextcloud }}"
|
||||||
|
- "{{ subdomain_office }}"
|
||||||
|
- "{{ subdomain_draw }}"
|
||||||
|
- "{{ subdomain_notes }}"
|
||||||
|
- "{{ subdomain_homarr }}"
|
||||||
|
- "{{ subdomain_dockhand }}"
|
||||||
|
- "{{ subdomain_uptime }}"
|
||||||
|
|
||||||
|
- name: Display DNS check results
|
||||||
|
debug:
|
||||||
|
msg: "{{ item.item }}.{{ domain }} → {{ item.stdout if item.stdout else 'NOT CONFIGURED' }}"
|
||||||
|
loop: "{{ dns_check.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}.{{ domain }}"
|
||||||
|
|
||||||
|
- name: Preflight checks complete
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ All preflight checks passed
|
||||||
|
✓ Ready to proceed with deployment
|
||||||
119
ansible/playbooks/02-system-setup.yml
Normal file
119
ansible/playbooks/02-system-setup.yml
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
# Playbook 02: System Setup
|
||||||
|
# Install packages, configure firewall and security
|
||||||
|
|
||||||
|
- name: System Setup
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Update apt cache
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Upgrade all packages
|
||||||
|
apt:
|
||||||
|
upgrade: dist
|
||||||
|
autoremove: yes
|
||||||
|
autoclean: yes
|
||||||
|
|
||||||
|
- name: Install essential packages
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- git
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- net-tools
|
||||||
|
- dnsutils
|
||||||
|
- ufw
|
||||||
|
- fail2ban
|
||||||
|
- unattended-upgrades
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
- lsb-release
|
||||||
|
- software-properties-common
|
||||||
|
- python3-pip
|
||||||
|
- python3-docker
|
||||||
|
- rsync
|
||||||
|
- "{% if install_rclone %}rclone{% endif %}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Configure UFW - Allow SSH
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: '22'
|
||||||
|
proto: tcp
|
||||||
|
|
||||||
|
- name: Configure UFW - Allow HTTP
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: '80'
|
||||||
|
proto: tcp
|
||||||
|
|
||||||
|
- name: Configure UFW - Allow HTTPS TCP
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: '443'
|
||||||
|
proto: tcp
|
||||||
|
|
||||||
|
- name: Configure UFW - Allow HTTPS UDP (HTTP/3)
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: '443'
|
||||||
|
proto: udp
|
||||||
|
|
||||||
|
- name: Configure UFW - Allow Tailscale
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: '41641'
|
||||||
|
proto: udp
|
||||||
|
|
||||||
|
- name: Enable UFW
|
||||||
|
ufw:
|
||||||
|
state: enabled
|
||||||
|
policy: deny
|
||||||
|
|
||||||
|
- name: Configure fail2ban for SSH
|
||||||
|
copy:
|
||||||
|
dest: /etc/fail2ban/jail.local
|
||||||
|
content: |
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
filter = sshd
|
||||||
|
logpath = /var/log/auth.log
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 600
|
||||||
|
notify: restart fail2ban
|
||||||
|
|
||||||
|
- name: Enable unattended security updates
|
||||||
|
copy:
|
||||||
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||||
|
content: |
|
||||||
|
APT::Periodic::Update-Package-Lists "1";
|
||||||
|
APT::Periodic::Unattended-Upgrade "1";
|
||||||
|
APT::Periodic::AutocleanInterval "7";
|
||||||
|
|
||||||
|
- name: Set timezone
|
||||||
|
timezone:
|
||||||
|
name: "{{ timezone }}"
|
||||||
|
|
||||||
|
- name: Set kernel parameters for Docker
|
||||||
|
sysctl:
|
||||||
|
name: "{{ item.key }}"
|
||||||
|
value: "{{ item.value }}"
|
||||||
|
state: present
|
||||||
|
reload: yes
|
||||||
|
loop:
|
||||||
|
- { key: 'net.ipv4.ip_forward', value: '1' }
|
||||||
|
- { key: 'fs.inotify.max_user_watches', value: '524288' }
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: restart fail2ban
|
||||||
|
service:
|
||||||
|
name: fail2ban
|
||||||
|
state: restarted
|
||||||
115
ansible/playbooks/03-docker-setup.yml
Normal file
115
ansible/playbooks/03-docker-setup.yml
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
# Playbook 03: Docker Setup
|
||||||
|
# Install Docker CE and Docker Compose v2
|
||||||
|
|
||||||
|
- name: Docker Installation
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Remove old Docker installations
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- docker
|
||||||
|
- docker-engine
|
||||||
|
- docker.io
|
||||||
|
- containerd
|
||||||
|
- runc
|
||||||
|
- docker-compose
|
||||||
|
state: absent
|
||||||
|
purge: yes
|
||||||
|
|
||||||
|
- name: Remove old Docker data directories
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /var/lib/docker
|
||||||
|
- /var/lib/containerd
|
||||||
|
- /etc/docker
|
||||||
|
|
||||||
|
- name: Add Docker GPG key
|
||||||
|
apt_key:
|
||||||
|
url: https://download.docker.com/linux/ubuntu/gpg
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add Docker repository
|
||||||
|
apt_repository:
|
||||||
|
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install Docker CE
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- docker-ce
|
||||||
|
- docker-ce-cli
|
||||||
|
- containerd.io
|
||||||
|
- docker-buildx-plugin
|
||||||
|
- docker-compose-plugin
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Create Docker daemon configuration
|
||||||
|
copy:
|
||||||
|
dest: /etc/docker/daemon.json
|
||||||
|
content: |
|
||||||
|
{
|
||||||
|
"log-driver": "json-file",
|
||||||
|
"log-opts": {
|
||||||
|
"max-size": "10m",
|
||||||
|
"max-file": "3"
|
||||||
|
},
|
||||||
|
"storage-driver": "overlay2",
|
||||||
|
"userland-proxy": false,
|
||||||
|
"live-restore": true
|
||||||
|
}
|
||||||
|
notify: restart docker
|
||||||
|
|
||||||
|
- name: Create docker group
|
||||||
|
group:
|
||||||
|
name: docker
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add user to docker group
|
||||||
|
user:
|
||||||
|
name: "{{ ansible_user }}"
|
||||||
|
groups: docker
|
||||||
|
append: yes
|
||||||
|
|
||||||
|
- name: Create deployment directory
|
||||||
|
file:
|
||||||
|
path: "{{ deployment_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Start and enable Docker
|
||||||
|
service:
|
||||||
|
name: docker
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
|
||||||
|
- name: Verify Docker installation
|
||||||
|
command: docker --version
|
||||||
|
register: docker_version
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display Docker version
|
||||||
|
debug:
|
||||||
|
msg: "Docker installed: {{ docker_version.stdout }}"
|
||||||
|
|
||||||
|
- name: Verify Docker Compose installation
|
||||||
|
command: docker compose version
|
||||||
|
register: compose_version
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display Docker Compose version
|
||||||
|
debug:
|
||||||
|
msg: "Docker Compose installed: {{ compose_version.stdout }}"
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: restart docker
|
||||||
|
service:
|
||||||
|
name: docker
|
||||||
|
state: restarted
|
||||||
60
ansible/playbooks/04-tailscale-setup.yml
Normal file
60
ansible/playbooks/04-tailscale-setup.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
# Playbook 04: Tailscale Setup
|
||||||
|
# Install and optionally activate Tailscale VPN
|
||||||
|
|
||||||
|
- name: Tailscale Installation
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Add Tailscale GPG key
|
||||||
|
apt_key:
|
||||||
|
url: https://pkgs.tailscale.com/stable/ubuntu/{{ ansible_distribution_release }}.noarmor.gpg
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add Tailscale repository
|
||||||
|
apt_repository:
|
||||||
|
repo: "deb https://pkgs.tailscale.com/stable/ubuntu {{ ansible_distribution_release }} main"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install Tailscale
|
||||||
|
apt:
|
||||||
|
name: tailscale
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Check if Tailscale auth key is provided
|
||||||
|
set_fact:
|
||||||
|
tailscale_auto_enable: "{{ tailscale_auth_key is defined and tailscale_auth_key != '' }}"
|
||||||
|
|
||||||
|
- name: Activate Tailscale (if auth key provided)
|
||||||
|
command: tailscale up --authkey={{ tailscale_auth_key }} --advertise-tags=tag:nextcloud
|
||||||
|
when: tailscale_auto_enable
|
||||||
|
register: tailscale_activation
|
||||||
|
|
||||||
|
- name: Get Tailscale IP (if activated)
|
||||||
|
command: tailscale ip -4
|
||||||
|
register: tailscale_ip
|
||||||
|
when: tailscale_auto_enable
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Display Tailscale status (activated)
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ Tailscale activated
|
||||||
|
IP: {{ tailscale_ip.stdout }}
|
||||||
|
when: tailscale_auto_enable
|
||||||
|
|
||||||
|
- name: Display manual activation instructions (not activated)
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
Tailscale installed but not activated.
|
||||||
|
To enable, run on the server:
|
||||||
|
sudo tailscale up
|
||||||
|
when: not tailscale_auto_enable
|
||||||
|
|
||||||
|
- name: Enable Tailscale service
|
||||||
|
service:
|
||||||
|
name: tailscaled
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
109
ansible/playbooks/05-deploy-stack.yml
Normal file
109
ansible/playbooks/05-deploy-stack.yml
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
# Playbook 05: Deploy Stack
|
||||||
|
# Deploy Docker Compose stack with all services
|
||||||
|
|
||||||
|
- name: Deploy Nextcloud Stack
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Create directory structure
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- "{{ deployment_dir }}/configs"
|
||||||
|
- "{{ deployment_dir }}/configs/caddy"
|
||||||
|
- "{{ deployment_dir }}/configs/nextcloud"
|
||||||
|
- "{{ deployment_dir }}/data"
|
||||||
|
- "{{ deployment_dir }}/data/homarr"
|
||||||
|
- "{{ deployment_dir }}/data/obsidian/config"
|
||||||
|
- "{{ deployment_dir }}/data/obsidian/vault"
|
||||||
|
- "{{ deployment_dir }}/backups"
|
||||||
|
- "{{ deployment_dir }}/backups/database"
|
||||||
|
- "{{ deployment_dir }}/backups/volumes"
|
||||||
|
|
||||||
|
- name: Template docker-compose.yml
|
||||||
|
template:
|
||||||
|
src: ../roles/nextcloud_stack/templates/docker-compose.yml.j2
|
||||||
|
dest: "{{ deployment_dir }}/docker-compose.yml"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Template .env file
|
||||||
|
template:
|
||||||
|
src: ../roles/nextcloud_stack/templates/env.j2
|
||||||
|
dest: "{{ deployment_dir }}/.env"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0600'
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Template Caddyfile
|
||||||
|
template:
|
||||||
|
src: ../roles/caddy/templates/Caddyfile.j2
|
||||||
|
dest: "{{ deployment_dir }}/configs/caddy/Caddyfile"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Pull Docker images
|
||||||
|
command: docker compose pull
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Start Docker Compose stack
|
||||||
|
command: docker compose up -d
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
|
||||||
|
- name: Wait for PostgreSQL to be healthy
|
||||||
|
command: docker exec next-db pg_isready -U {{ db_user }}
|
||||||
|
register: pg_ready
|
||||||
|
until: pg_ready.rc == 0
|
||||||
|
retries: 30
|
||||||
|
delay: 10
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Wait for Nextcloud to be accessible
|
||||||
|
uri:
|
||||||
|
url: http://localhost:8808/status.php
|
||||||
|
status_code: 200
|
||||||
|
register: nc_status
|
||||||
|
until: nc_status.status == 200
|
||||||
|
retries: 30
|
||||||
|
delay: 10
|
||||||
|
|
||||||
|
- name: Configure Nextcloud Redis cache
|
||||||
|
command: |
|
||||||
|
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Configure Nextcloud Redis port
|
||||||
|
command: |
|
||||||
|
docker exec -u www-data next php occ config:system:set redis port --value=6379
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Configure Nextcloud memcache
|
||||||
|
command: |
|
||||||
|
docker exec -u www-data next php occ config:system:set memcache.locking --value='\\OC\\Memcache\\Redis'
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Display deployment success
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ Docker Compose stack deployed
|
||||||
|
✓ All containers started
|
||||||
|
✓ Nextcloud is accessible
|
||||||
48
ansible/playbooks/06-configure-caddy.yml
Normal file
48
ansible/playbooks/06-configure-caddy.yml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
# Playbook 06: Configure Caddy
|
||||||
|
# Setup reverse proxy and obtain SSL certificates
|
||||||
|
|
||||||
|
- name: Configure Caddy Reverse Proxy
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Validate Caddyfile syntax
|
||||||
|
command: docker exec caddy caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
register: caddy_validate
|
||||||
|
failed_when: caddy_validate.rc != 0
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Reload Caddy configuration
|
||||||
|
command: docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
|
||||||
|
- name: Wait for SSL certificates (may take 1-2 minutes)
|
||||||
|
pause:
|
||||||
|
seconds: 30
|
||||||
|
prompt: "Waiting for Let's Encrypt to issue certificates..."
|
||||||
|
|
||||||
|
- name: Test HTTPS endpoint for Nextcloud
|
||||||
|
uri:
|
||||||
|
url: "https://{{ subdomain_nextcloud }}.{{ domain }}/status.php"
|
||||||
|
validate_certs: yes
|
||||||
|
status_code: 200
|
||||||
|
register: https_test
|
||||||
|
until: https_test.status == 200
|
||||||
|
retries: 10
|
||||||
|
delay: 10
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Display Caddy status
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ Caddyfile validated
|
||||||
|
✓ Caddy reloaded
|
||||||
|
{% if https_test.status == 200 %}
|
||||||
|
✓ HTTPS working: https://{{ subdomain_nextcloud }}.{{ domain }}
|
||||||
|
{% else %}
|
||||||
|
⚠ HTTPS check failed - verify DNS and firewall
|
||||||
|
{% endif %}
|
||||||
87
ansible/playbooks/07-setup-backups.yml
Normal file
87
ansible/playbooks/07-setup-backups.yml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
# Playbook 07: Setup Backups
|
||||||
|
# Configure automated backup system
|
||||||
|
|
||||||
|
- name: Setup Backup System
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Create backup script
|
||||||
|
copy:
|
||||||
|
dest: "{{ deployment_dir }}/backup.sh"
|
||||||
|
owner: "{{ ansible_user }}"
|
||||||
|
group: "{{ ansible_user }}"
|
||||||
|
mode: '0755'
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
# Nextcloud Stack Backup Script
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BACKUP_DIR="{{ deployment_dir }}/backups"
|
||||||
|
DB_BACKUP_DIR="$BACKUP_DIR/database"
|
||||||
|
VOLUME_BACKUP_DIR="$BACKUP_DIR/volumes"
|
||||||
|
RETENTION_DAYS={{ backup_retention_days }}
|
||||||
|
DATE=$(date +%Y%m%d_%H%M%S)
|
||||||
|
LOG_FILE="$BACKUP_DIR/backup.log"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "Starting backup process..."
|
||||||
|
|
||||||
|
# Database backup
|
||||||
|
log "Backing up PostgreSQL database..."
|
||||||
|
docker exec next-db pg_dump -U {{ db_user }} {{ db_name }} | \
|
||||||
|
gzip > "$DB_BACKUP_DIR/nextcloud_db_$DATE.sql.gz"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "Database backup completed: nextcloud_db_$DATE.sql.gz"
|
||||||
|
else
|
||||||
|
log "ERROR: Database backup failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Weekly volume backup (Sundays only)
|
||||||
|
if [ $(date +%u) -eq 7 ]; then
|
||||||
|
log "Weekly volume backup (Sunday)..."
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --on
|
||||||
|
|
||||||
|
tar -czf "$VOLUME_BACKUP_DIR/nextcloud_data_$DATE.tar.gz" \
|
||||||
|
-C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data . 2>/dev/null || true
|
||||||
|
|
||||||
|
tar -czf "$VOLUME_BACKUP_DIR/configs_$DATE.tar.gz" \
|
||||||
|
-C {{ deployment_dir }}/configs . 2>/dev/null || true
|
||||||
|
|
||||||
|
docker exec -u www-data next php occ maintenance:mode --off
|
||||||
|
log "Volume backup completed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup old backups
|
||||||
|
log "Cleaning up backups older than $RETENTION_DAYS days..."
|
||||||
|
find "$DB_BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
|
||||||
|
find "$VOLUME_BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
|
||||||
|
|
||||||
|
log "Backup process completed successfully!"
|
||||||
|
|
||||||
|
- name: Create cron job for daily backups
|
||||||
|
cron:
|
||||||
|
name: "Nextcloud Stack Backup"
|
||||||
|
minute: "0"
|
||||||
|
hour: "3"
|
||||||
|
job: "{{ deployment_dir }}/backup.sh >> {{ deployment_dir }}/backups/backup.log 2>&1"
|
||||||
|
user: root
|
||||||
|
|
||||||
|
- name: Run initial test backup
|
||||||
|
command: "{{ deployment_dir }}/backup.sh"
|
||||||
|
register: backup_test
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Display backup status
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ Backup script created
|
||||||
|
✓ Cron job configured (daily 3:00 AM)
|
||||||
|
✓ Test backup completed
|
||||||
|
Location: {{ deployment_dir }}/backups/
|
||||||
154
ansible/playbooks/08-post-deployment.yml
Normal file
154
ansible/playbooks/08-post-deployment.yml
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
# Playbook 08: Post-Deployment
|
||||||
|
# Final verification and configuration
|
||||||
|
|
||||||
|
- name: Post-Deployment Tasks
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Verify all containers are running
|
||||||
|
command: docker compose ps --format json
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
register: container_status
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Check Nextcloud status
|
||||||
|
command: docker exec -u www-data next php occ status
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
register: nc_status
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Install recommended Nextcloud apps
|
||||||
|
command: docker exec -u www-data next php occ app:install {{ item }}
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
loop:
|
||||||
|
- calendar
|
||||||
|
- contacts
|
||||||
|
- tasks
|
||||||
|
- notes
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Configure Nextcloud background jobs
|
||||||
|
command: docker exec -u www-data next php occ background:cron
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Setup Nextcloud cron job
|
||||||
|
cron:
|
||||||
|
name: "Nextcloud Background Jobs"
|
||||||
|
minute: "*/5"
|
||||||
|
job: "docker exec -u www-data next php /var/www/html/cron.php"
|
||||||
|
user: root
|
||||||
|
|
||||||
|
- name: Get Tailscale IP (if activated)
|
||||||
|
command: tailscale ip -4
|
||||||
|
register: tailscale_ip_result
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Create deployment report
|
||||||
|
copy:
|
||||||
|
dest: "{{ deployment_dir }}/DEPLOYMENT.txt"
|
||||||
|
content: |
|
||||||
|
════════════════════════════════════════════════════════════
|
||||||
|
NEXTCLOUD STACK DEPLOYMENT REPORT
|
||||||
|
════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
Server: {{ inventory_hostname }}
|
||||||
|
IP Address: {{ ansible_host }}
|
||||||
|
Domain: {{ domain }}
|
||||||
|
Deployment Date: {{ ansible_date_time.iso8601 }}
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
PUBLIC SERVICES (HTTPS)
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
|
||||||
|
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
|
||||||
|
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
|
||||||
|
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
MANAGEMENT SERVICES (Tailscale only)
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
|
||||||
|
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
|
||||||
|
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
CREDENTIALS
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
Nextcloud Admin User: {{ admin_user }}
|
||||||
|
Nextcloud Admin Password: [stored in Ansible vault]
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
TAILSCALE
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
{% if tailscale_ip_result.rc == 0 %}
|
||||||
|
Status: Connected
|
||||||
|
Tailscale IP: {{ tailscale_ip_result.stdout }}
|
||||||
|
{% else %}
|
||||||
|
Status: Not activated
|
||||||
|
Activate with: sudo tailscale up
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
BACKUPS
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
Schedule: Daily at 3:00 AM
|
||||||
|
Location: {{ deployment_dir }}/backups/
|
||||||
|
Retention: {{ backup_retention_days }} days
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
USEFUL COMMANDS
|
||||||
|
────────────────────────────────────────────────────────────
|
||||||
|
View containers: cd {{ deployment_dir }} && docker compose ps
|
||||||
|
View logs: cd {{ deployment_dir }} && docker compose logs -f
|
||||||
|
Restart service: cd {{ deployment_dir }} && docker compose restart [service]
|
||||||
|
Manual backup: {{ deployment_dir }}/backup.sh
|
||||||
|
Nextcloud CLI: docker exec -u www-data next php occ [command]
|
||||||
|
|
||||||
|
════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
- name: Display deployment summary
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
╔════════════════════════════════════════════════════════════╗
|
||||||
|
║ DEPLOYMENT COMPLETED SUCCESSFULLY! ║
|
||||||
|
╚════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
Server: {{ inventory_hostname }} ({{ ansible_host }})
|
||||||
|
Domain: {{ domain }}
|
||||||
|
|
||||||
|
📦 Public Services:
|
||||||
|
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
|
||||||
|
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
|
||||||
|
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
|
||||||
|
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
|
||||||
|
|
||||||
|
🔒 Management (Tailscale only):
|
||||||
|
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
|
||||||
|
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
|
||||||
|
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
|
||||||
|
|
||||||
|
👤 Nextcloud Admin:
|
||||||
|
Username: {{ admin_user }}
|
||||||
|
Password: [check vault]
|
||||||
|
|
||||||
|
💾 Backups:
|
||||||
|
Daily at 3:00 AM
|
||||||
|
Location: {{ deployment_dir }}/backups/
|
||||||
|
|
||||||
|
📝 Next Steps:
|
||||||
|
1. Login to Nextcloud and complete setup
|
||||||
|
2. Setup Uptime Kuma monitoring (via Tailscale)
|
||||||
|
3. Configure Homarr dashboard (via Tailscale)
|
||||||
|
4. Review deployment report: {{ deployment_dir }}/DEPLOYMENT.txt
|
||||||
|
|
||||||
|
Deployment report saved to:
|
||||||
|
{{ deployment_dir }}/DEPLOYMENT.txt
|
||||||
41
ansible/playbooks/99-rollback.yml
Normal file
41
ansible/playbooks/99-rollback.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
# Playbook 99: Emergency Rollback
|
||||||
|
# Stop and remove the deployment (keeps backups)
|
||||||
|
|
||||||
|
- name: Emergency Rollback
|
||||||
|
hosts: all
|
||||||
|
become: yes
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Confirm rollback
|
||||||
|
pause:
|
||||||
|
prompt: |
|
||||||
|
⚠️ WARNING: This will stop and remove all containers!
|
||||||
|
Backups in {{ deployment_dir }}/backups/ will be preserved.
|
||||||
|
Press ENTER to continue or Ctrl+C to cancel
|
||||||
|
|
||||||
|
- name: Stop Docker Compose stack
|
||||||
|
command: docker compose down
|
||||||
|
args:
|
||||||
|
chdir: "{{ deployment_dir }}"
|
||||||
|
become_user: "{{ ansible_user }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Remove deployment directory (except backups)
|
||||||
|
shell: |
|
||||||
|
cd {{ deployment_dir }}
|
||||||
|
rm -rf configs data docker-compose.yml .env
|
||||||
|
ls -la
|
||||||
|
args:
|
||||||
|
warn: false
|
||||||
|
|
||||||
|
- name: Display rollback status
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
✓ Containers stopped and removed
|
||||||
|
✓ Deployment files removed
|
||||||
|
✓ Backups preserved in {{ deployment_dir }}/backups/
|
||||||
|
✓ Docker volumes preserved (use 'docker volume ls' to see)
|
||||||
|
|
||||||
|
To completely remove volumes (DELETES ALL DATA):
|
||||||
|
cd {{ deployment_dir }} && docker compose down -v
|
||||||
26
ansible/playbooks/site.yml
Normal file
26
ansible/playbooks/site.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# Main Playbook - Nextcloud Stack Deployment
|
||||||
|
# This orchestrates the complete deployment process
|
||||||
|
|
||||||
|
- name: Nextcloud Stack - Complete Deployment
|
||||||
|
hosts: all
|
||||||
|
gather_facts: yes
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Display deployment banner
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
╔════════════════════════════════════════════════════════════╗
|
||||||
|
║ Nextcloud Stack Deployment Starting ║
|
||||||
|
║ Target: {{ inventory_hostname }} ({{ ansible_host }}) ║
|
||||||
|
║ Domain: {{ domain }} ║
|
||||||
|
╚════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
- import_playbook: 01-preflight-checks.yml
|
||||||
|
- import_playbook: 02-system-setup.yml
|
||||||
|
- import_playbook: 03-docker-setup.yml
|
||||||
|
- import_playbook: 04-tailscale-setup.yml
|
||||||
|
- import_playbook: 05-deploy-stack.yml
|
||||||
|
- import_playbook: 06-configure-caddy.yml
|
||||||
|
- import_playbook: 07-setup-backups.yml
|
||||||
|
- import_playbook: 08-post-deployment.yml
|
||||||
128
ansible/roles/caddy/templates/Caddyfile.j2
Normal file
128
ansible/roles/caddy/templates/Caddyfile.j2
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Caddyfile - Generated by Ansible
|
||||||
|
# Domain: {{ domain }}
|
||||||
|
|
||||||
|
# Global options
|
||||||
|
{
|
||||||
|
email {{ user_email }}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== PUBLIC SERVICES =====
|
||||||
|
|
||||||
|
# Nextcloud
|
||||||
|
{{ subdomain_nextcloud }}.{{ domain }} {
|
||||||
|
reverse_proxy next:80
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
|
X-Content-Type-Options nosniff
|
||||||
|
X-Frame-Options SAMEORIGIN
|
||||||
|
Referrer-Policy no-referrer
|
||||||
|
X-XSS-Protection "1; mode=block"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
|
||||||
|
redir /.well-known/carddav /remote.php/dav 301
|
||||||
|
redir /.well-known/caldav /remote.php/dav 301
|
||||||
|
redir /.well-known/webfinger /index.php/.well-known/webfinger 301
|
||||||
|
redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301
|
||||||
|
|
||||||
|
request_body {
|
||||||
|
max_size 10GB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# OnlyOffice Document Server
|
||||||
|
{{ subdomain_office }}.{{ domain }} {
|
||||||
|
reverse_proxy onlyoffice:80
|
||||||
|
|
||||||
|
request_body {
|
||||||
|
max_size 100MB
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Excalidraw
|
||||||
|
{{ subdomain_draw }}.{{ domain }} {
|
||||||
|
reverse_proxy excalidraw:80
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Obsidian
|
||||||
|
{{ subdomain_notes }}.{{ domain }} {
|
||||||
|
reverse_proxy obsidian:3000
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===== TAILSCALE-ONLY SERVICES =====
|
||||||
|
|
||||||
|
# Homarr Dashboard
|
||||||
|
{{ subdomain_homarr }}.{{ domain }} {
|
||||||
|
@tailscale {
|
||||||
|
remote_ip 100.64.0.0/10
|
||||||
|
}
|
||||||
|
|
||||||
|
handle @tailscale {
|
||||||
|
reverse_proxy homarr:7575
|
||||||
|
}
|
||||||
|
|
||||||
|
handle {
|
||||||
|
respond "Access Denied - Tailscale Required" 403
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dockhand Container Manager
|
||||||
|
{{ subdomain_dockhand }}.{{ domain }} {
|
||||||
|
@tailscale {
|
||||||
|
remote_ip 100.64.0.0/10
|
||||||
|
}
|
||||||
|
|
||||||
|
handle @tailscale {
|
||||||
|
reverse_proxy dockhand:3000
|
||||||
|
}
|
||||||
|
|
||||||
|
handle {
|
||||||
|
respond "Access Denied - Tailscale Required" 403
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uptime Kuma Monitoring
|
||||||
|
{{ subdomain_uptime }}.{{ domain }} {
|
||||||
|
@tailscale {
|
||||||
|
remote_ip 100.64.0.0/10
|
||||||
|
}
|
||||||
|
|
||||||
|
handle @tailscale {
|
||||||
|
reverse_proxy uptime-kuma:3001
|
||||||
|
}
|
||||||
|
|
||||||
|
handle {
|
||||||
|
respond "Access Denied - Tailscale Required" 403
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if enable_public_status %}
|
||||||
|
# Public Status Page
|
||||||
|
status.{{ domain }} {
|
||||||
|
reverse_proxy uptime-kuma:3001/status
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
229
ansible/roles/nextcloud_stack/templates/docker-compose.yml.j2
Normal file
229
ansible/roles/nextcloud_stack/templates/docker-compose.yml.j2
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
services:
|
||||||
|
# ===== DATABASE LAYER =====
|
||||||
|
|
||||||
|
next-db:
|
||||||
|
image: docker.io/postgres:18
|
||||||
|
container_name: next-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB={{ db_name }}
|
||||||
|
- POSTGRES_USER={{ db_user }}
|
||||||
|
- POSTGRES_PASSWORD={{ db_password }}
|
||||||
|
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
||||||
|
volumes:
|
||||||
|
- pg_data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
next-redis:
|
||||||
|
image: docker.io/redis:7-alpine
|
||||||
|
container_name: next-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server --requirepass {{ redis_password }} --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# ===== APPLICATION LAYER =====
|
||||||
|
|
||||||
|
next:
|
||||||
|
image: docker.io/nextcloud:latest
|
||||||
|
container_name: next
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
next-db:
|
||||||
|
condition: service_healthy
|
||||||
|
next-redis:
|
||||||
|
condition: service_healthy
|
||||||
|
mem_swappiness: -1
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB={{ db_name }}
|
||||||
|
- POSTGRES_USER={{ db_user }}
|
||||||
|
- POSTGRES_PASSWORD={{ db_password }}
|
||||||
|
- POSTGRES_HOST=next-db
|
||||||
|
- REDIS_HOST=next-redis
|
||||||
|
- REDIS_HOST_PASSWORD={{ redis_password }}
|
||||||
|
- NEXTCLOUD_TRUSTED_DOMAINS={{ subdomain_nextcloud }}.{{ domain }}
|
||||||
|
- NEXTCLOUD_ADMIN_USER={{ admin_user }}
|
||||||
|
- NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
|
||||||
|
- OVERWRITEPROTOCOL=https
|
||||||
|
- OVERWRITEHOST={{ subdomain_nextcloud }}.{{ domain }}
|
||||||
|
- TRUSTED_PROXIES=caddy
|
||||||
|
- PHP_MEMORY_LIMIT=512M
|
||||||
|
- PHP_UPLOAD_LIMIT=10G
|
||||||
|
volumes:
|
||||||
|
- nextcloud_data:/var/www/html
|
||||||
|
- {{ deployment_dir }}/configs/nextcloud:/var/www/html/config:Z
|
||||||
|
- nextcloud_apps:/var/www/html/custom_apps
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=false"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost/status.php"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
onlyoffice:
|
||||||
|
image: docker.io/onlyoffice/documentserver:latest
|
||||||
|
container_name: onlyoffice
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- JWT_ENABLED=false
|
||||||
|
- JWT_HEADER=Authorization
|
||||||
|
- JWT_IN_BODY=true
|
||||||
|
volumes:
|
||||||
|
- onlyoffice_data:/var/www/onlyoffice/Data
|
||||||
|
- onlyoffice_logs:/var/log/onlyoffice
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.monitor-only=true"
|
||||||
|
|
||||||
|
excalidraw:
|
||||||
|
image: docker.io/excalidraw/excalidraw:latest
|
||||||
|
container_name: excalidraw
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- TZ={{ timezone }}
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
|
||||||
|
obsidian:
|
||||||
|
image: lscr.io/linuxserver/obsidian:latest
|
||||||
|
container_name: obsidian
|
||||||
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- seccomp:unconfined
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ={{ timezone }}
|
||||||
|
volumes:
|
||||||
|
- {{ deployment_dir }}/data/obsidian/config:/config:z
|
||||||
|
- {{ deployment_dir }}/data/obsidian/vault:/vault:z
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
shm_size: "1gb"
|
||||||
|
|
||||||
|
# ===== INFRASTRUCTURE LAYER =====
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: docker.io/caddy:latest
|
||||||
|
container_name: caddy
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "443:443/udp"
|
||||||
|
volumes:
|
||||||
|
- {{ deployment_dir }}/configs/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
environment:
|
||||||
|
- DOMAIN={{ domain }}
|
||||||
|
- EMAIL={{ user_email }}
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
|
||||||
|
homarr:
|
||||||
|
image: ghcr.io/homarr-labs/homarr:latest
|
||||||
|
container_name: homarr
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- {{ deployment_dir }}/data/homarr:/appdata
|
||||||
|
environment:
|
||||||
|
- SECRET_ENCRYPTION_KEY={{ homarr_secret }}
|
||||||
|
- TZ={{ timezone }}
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
|
||||||
|
dockhand:
|
||||||
|
image: fnsys/dockhand:latest
|
||||||
|
container_name: dockhand
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3003:3000"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- dockhand_data:/app/data
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
|
||||||
|
uptime-kuma:
|
||||||
|
image: louislam/uptime-kuma:latest
|
||||||
|
container_name: uptime-kuma
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- uptime_kuma_data:/app/data
|
||||||
|
environment:
|
||||||
|
- TZ={{ timezone }}
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower:latest
|
||||||
|
container_name: watchtower
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- WATCHTOWER_CLEANUP=true
|
||||||
|
- WATCHTOWER_LABEL_ENABLE=true
|
||||||
|
- WATCHTOWER_POLL_INTERVAL=86400
|
||||||
|
- WATCHTOWER_ROLLING_RESTART=true
|
||||||
|
- WATCHTOWER_INCLUDE_RESTARTING=true
|
||||||
|
- TZ={{ timezone }}
|
||||||
|
networks:
|
||||||
|
- nextcloud_network
|
||||||
|
|
||||||
|
# ===== PERSISTENT STORAGE =====
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pg_data:
|
||||||
|
redis_data:
|
||||||
|
nextcloud_data:
|
||||||
|
nextcloud_apps:
|
||||||
|
onlyoffice_data:
|
||||||
|
onlyoffice_logs:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
dockhand_data:
|
||||||
|
uptime_kuma_data:
|
||||||
|
|
||||||
|
# ===== NETWORKING =====
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nextcloud_network:
|
||||||
|
name: nextcloud_network
|
||||||
|
driver: bridge
|
||||||
31
ansible/roles/nextcloud_stack/templates/env.j2
Normal file
31
ansible/roles/nextcloud_stack/templates/env.j2
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Environment Variables for Nextcloud Stack
|
||||||
|
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
DB_NAME={{ db_name }}
|
||||||
|
DB_USER={{ db_user }}
|
||||||
|
DB_PASSWORD={{ db_password }}
|
||||||
|
DB_HOST=next-db
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
|
REDIS_PASSWORD={{ redis_password }}
|
||||||
|
|
||||||
|
# Nextcloud Admin
|
||||||
|
NEXTCLOUD_ADMIN_USER={{ admin_user }}
|
||||||
|
NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
|
||||||
|
|
||||||
|
# Application Secrets
|
||||||
|
HOMARR_SECRET_KEY={{ homarr_secret }}
|
||||||
|
|
||||||
|
# Domain Configuration
|
||||||
|
DOMAIN={{ domain }}
|
||||||
|
SUBDOMAIN_NEXTCLOUD={{ subdomain_nextcloud }}
|
||||||
|
SUBDOMAIN_OFFICE={{ subdomain_office }}
|
||||||
|
|
||||||
|
# User Configuration
|
||||||
|
USER_EMAIL={{ user_email }}
|
||||||
|
TIMEZONE={{ timezone }}
|
||||||
|
|
||||||
|
# UIDs/GIDs
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
463
ansible/setup.sh
Executable file
463
ansible/setup.sh
Executable file
@@ -0,0 +1,463 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Nextcloud Stack - Interactive Setup Script
|
||||||
|
# This script collects all configuration variables and generates Ansible inventory
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Header
|
||||||
|
clear
|
||||||
|
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${CYAN}║ Nextcloud Stack - Ansible Deployment Setup ║${NC}"
|
||||||
|
echo -e "${CYAN}║ Version 1.0 ║${NC}"
|
||||||
|
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This script will guide you through configuring your Nextcloud"
|
||||||
|
echo "deployment. Information will be encrypted using Ansible Vault."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Arrays to store server data
|
||||||
|
declare -a SERVER_IPS
|
||||||
|
declare -a SERVER_HOSTNAMES
|
||||||
|
declare -a SERVER_DOMAINS
|
||||||
|
declare -a SERVER_SSH_USERS
|
||||||
|
declare -a SERVER_SSH_KEYS
|
||||||
|
|
||||||
|
# Global variables
|
||||||
|
ADMIN_USER=""
|
||||||
|
ADMIN_PASSWORD=""
|
||||||
|
USER_NAME=""
|
||||||
|
USER_EMAIL=""
|
||||||
|
TIMEZONE="Europe/Zurich"
|
||||||
|
INSTALL_RCLONE="y"
|
||||||
|
ENABLE_PUBLIC_STATUS="n"
|
||||||
|
ALERT_EMAIL=""
|
||||||
|
TAILSCALE_AUTH_KEY=""
|
||||||
|
|
||||||
|
# Subdomain defaults
|
||||||
|
SUBDOMAIN_NEXTCLOUD="cloud"
|
||||||
|
SUBDOMAIN_OFFICE="office"
|
||||||
|
SUBDOMAIN_DRAW="draw"
|
||||||
|
SUBDOMAIN_NOTES="notes"
|
||||||
|
SUBDOMAIN_HOMARR="home"
|
||||||
|
SUBDOMAIN_DOCKHAND="manage"
|
||||||
|
SUBDOMAIN_UPTIME="uptime"
|
||||||
|
|
||||||
|
# Database defaults
|
||||||
|
DB_NAME="nextcloud"
|
||||||
|
DB_USER="nextcloud"
|
||||||
|
|
||||||
|
# Generate random password
|
||||||
|
generate_password() {
|
||||||
|
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate IP address
|
||||||
|
validate_ip() {
|
||||||
|
local ip=$1
|
||||||
|
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate domain
|
||||||
|
validate_domain() {
|
||||||
|
local domain=$1
|
||||||
|
if [[ $domain =~ ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$ ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Server Configuration
|
||||||
|
echo -e "${GREEN}=== Server Configuration ===${NC}"
|
||||||
|
echo "Enter server details (press Enter without input to finish):"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
server_count=0
|
||||||
|
while true; do
|
||||||
|
((server_count++))
|
||||||
|
echo -e "${BLUE}Server $server_count:${NC}"
|
||||||
|
|
||||||
|
# IP Address
|
||||||
|
while true; do
|
||||||
|
read -p " IP address: " ip
|
||||||
|
if [[ -z "$ip" ]]; then
|
||||||
|
((server_count--))
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
if validate_ip "$ip"; then
|
||||||
|
SERVER_IPS+=("$ip")
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo -e "${RED} Invalid IP address. Please try again.${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Hostname
|
||||||
|
read -p " Hostname [cloud$(printf "%02d" $server_count)]: " hostname
|
||||||
|
hostname=${hostname:-cloud$(printf "%02d" $server_count)}
|
||||||
|
SERVER_HOSTNAMES+=("$hostname")
|
||||||
|
|
||||||
|
# Domain
|
||||||
|
while true; do
|
||||||
|
read -p " Domain (e.g., example.com): " domain
|
||||||
|
if [[ -z "$domain" ]]; then
|
||||||
|
echo -e "${RED} Domain is required. Please try again.${NC}"
|
||||||
|
elif validate_domain "$domain"; then
|
||||||
|
SERVER_DOMAINS+=("$domain")
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo -e "${RED} Invalid domain format. Please try again.${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# SSH User
|
||||||
|
read -p " SSH user [root]: " ssh_user
|
||||||
|
ssh_user=${ssh_user:-root}
|
||||||
|
SERVER_SSH_USERS+=("$ssh_user")
|
||||||
|
|
||||||
|
# SSH Key
|
||||||
|
read -p " SSH key path [~/.ssh/id_rsa]: " ssh_key
|
||||||
|
ssh_key=${ssh_key:-~/.ssh/id_rsa}
|
||||||
|
# Expand tilde
|
||||||
|
ssh_key="${ssh_key/#\~/$HOME}"
|
||||||
|
SERVER_SSH_KEYS+=("$ssh_key")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#SERVER_IPS[@]} -eq 0 ]]; then
|
||||||
|
echo -e "${RED}Error: At least one server is required.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Configured ${#SERVER_IPS[@]} server(s)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# User Information
|
||||||
|
echo -e "${GREEN}=== User Information ===${NC}"
|
||||||
|
read -p "Your name: " USER_NAME
|
||||||
|
while [[ -z "$USER_NAME" ]]; do
|
||||||
|
echo -e "${RED}Name is required.${NC}"
|
||||||
|
read -p "Your name: " USER_NAME
|
||||||
|
done
|
||||||
|
|
||||||
|
read -p "Your email: " USER_EMAIL
|
||||||
|
while [[ -z "$USER_EMAIL" ]] || [[ ! "$USER_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
|
||||||
|
echo -e "${RED}Valid email is required.${NC}"
|
||||||
|
read -p "Your email: " USER_EMAIL
|
||||||
|
done
|
||||||
|
|
||||||
|
read -p "Timezone [Europe/Zurich]: " TIMEZONE
|
||||||
|
TIMEZONE=${TIMEZONE:-Europe/Zurich}
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Nextcloud Admin Account
|
||||||
|
echo -e "${GREEN}=== Nextcloud Admin Account ===${NC}"
|
||||||
|
read -p "Admin username [admin]: " ADMIN_USER
|
||||||
|
ADMIN_USER=${ADMIN_USER:-admin}
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -sp "Admin password: " ADMIN_PASSWORD
|
||||||
|
echo ""
|
||||||
|
read -sp "Confirm password: " ADMIN_PASSWORD_CONFIRM
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]] && [[ ${#ADMIN_PASSWORD} -ge 8 ]]; then
|
||||||
|
break
|
||||||
|
elif [[ ${#ADMIN_PASSWORD} -lt 8 ]]; then
|
||||||
|
echo -e "${RED}Password must be at least 8 characters.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Passwords do not match. Please try again.${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Subdomain Configuration
|
||||||
|
echo -e "${GREEN}=== Subdomain Configuration ===${NC}"
|
||||||
|
echo "Press Enter to use defaults shown in brackets"
|
||||||
|
read -p "Nextcloud subdomain [cloud]: " SUBDOMAIN_NEXTCLOUD
|
||||||
|
SUBDOMAIN_NEXTCLOUD=${SUBDOMAIN_NEXTCLOUD:-cloud}
|
||||||
|
|
||||||
|
read -p "OnlyOffice subdomain [office]: " SUBDOMAIN_OFFICE
|
||||||
|
SUBDOMAIN_OFFICE=${SUBDOMAIN_OFFICE:-office}
|
||||||
|
|
||||||
|
read -p "Excalidraw subdomain [draw]: " SUBDOMAIN_DRAW
|
||||||
|
SUBDOMAIN_DRAW=${SUBDOMAIN_DRAW:-draw}
|
||||||
|
|
||||||
|
read -p "Obsidian subdomain [notes]: " SUBDOMAIN_NOTES
|
||||||
|
SUBDOMAIN_NOTES=${SUBDOMAIN_NOTES:-notes}
|
||||||
|
|
||||||
|
read -p "Homarr subdomain [home]: " SUBDOMAIN_HOMARR
|
||||||
|
SUBDOMAIN_HOMARR=${SUBDOMAIN_HOMARR:-home}
|
||||||
|
|
||||||
|
read -p "Dockhand subdomain [manage]: " SUBDOMAIN_DOCKHAND
|
||||||
|
SUBDOMAIN_DOCKHAND=${SUBDOMAIN_DOCKHAND:-manage}
|
||||||
|
|
||||||
|
read -p "Uptime Kuma subdomain [uptime]: " SUBDOMAIN_UPTIME
|
||||||
|
SUBDOMAIN_UPTIME=${SUBDOMAIN_UPTIME:-uptime}
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
echo -e "${GREEN}=== Database Configuration ===${NC}"
|
||||||
|
read -p "Database name [nextcloud]: " DB_NAME
|
||||||
|
DB_NAME=${DB_NAME:-nextcloud}
|
||||||
|
|
||||||
|
read -p "Database user [nextcloud]: " DB_USER
|
||||||
|
DB_USER=${DB_USER:-nextcloud}
|
||||||
|
|
||||||
|
echo "Generating secure passwords..."
|
||||||
|
DB_PASSWORD=$(generate_password)
|
||||||
|
REDIS_PASSWORD=$(generate_password)
|
||||||
|
HOMARR_SECRET=$(openssl rand -hex 32)
|
||||||
|
echo -e "${GREEN}✓ Passwords generated${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Tailscale Configuration
|
||||||
|
echo -e "${GREEN}=== Tailscale Configuration ===${NC}"
|
||||||
|
read -p "Install Tailscale? (y/n) [y]: " install_tailscale
|
||||||
|
install_tailscale=${install_tailscale:-y}
|
||||||
|
|
||||||
|
if [[ "$install_tailscale" == "y" ]]; then
|
||||||
|
read -p "Tailscale auth key (optional, press Enter to skip): " TAILSCALE_AUTH_KEY
|
||||||
|
if [[ -z "$TAILSCALE_AUTH_KEY" ]]; then
|
||||||
|
echo "Note: Tailscale will be installed but not activated."
|
||||||
|
echo " Activate manually with: sudo tailscale up"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Monitoring Configuration
|
||||||
|
echo -e "${GREEN}=== Monitoring Configuration ===${NC}"
|
||||||
|
read -p "Email for uptime alerts: " ALERT_EMAIL
|
||||||
|
while [[ -z "$ALERT_EMAIL" ]] || [[ ! "$ALERT_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
|
||||||
|
echo -e "${RED}Valid email is required.${NC}"
|
||||||
|
read -p "Email for uptime alerts: " ALERT_EMAIL
|
||||||
|
done
|
||||||
|
|
||||||
|
read -p "Enable public status page? (y/n) [n]: " ENABLE_PUBLIC_STATUS
|
||||||
|
ENABLE_PUBLIC_STATUS=${ENABLE_PUBLIC_STATUS:-n}
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup Configuration
|
||||||
|
echo -e "${GREEN}=== Backup Configuration ===${NC}"
|
||||||
|
echo "Backup retention: 30 days (default)"
|
||||||
|
read -p "Install rclone for future remote backups? (y/n) [y]: " INSTALL_RCLONE
|
||||||
|
INSTALL_RCLONE=${INSTALL_RCLONE:-y}
|
||||||
|
|
||||||
|
if [[ "$INSTALL_RCLONE" == "y" ]]; then
|
||||||
|
echo "Note: rclone will be installed but not configured."
|
||||||
|
echo " Configure later with: rclone config"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Ansible Vault Password
|
||||||
|
echo -e "${GREEN}=== Security ===${NC}"
|
||||||
|
echo "Create Ansible Vault password (will encrypt all secrets):"
|
||||||
|
while true; do
|
||||||
|
read -sp "Vault password: " VAULT_PASSWORD
|
||||||
|
echo ""
|
||||||
|
read -sp "Confirm: " VAULT_PASSWORD_CONFIRM
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ "$VAULT_PASSWORD" == "$VAULT_PASSWORD_CONFIRM" ]] && [[ ${#VAULT_PASSWORD} -ge 8 ]]; then
|
||||||
|
break
|
||||||
|
elif [[ ${#VAULT_PASSWORD} -lt 8 ]]; then
|
||||||
|
echo -e "${RED}Password must be at least 8 characters.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Passwords do not match. Please try again.${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Configuration Summary
|
||||||
|
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${CYAN}║ CONFIGURATION SUMMARY ║${NC}"
|
||||||
|
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Servers: ${#SERVER_IPS[@]}"
|
||||||
|
for i in "${!SERVER_IPS[@]}"; do
|
||||||
|
echo " • ${SERVER_IPS[$i]} (${SERVER_DOMAINS[$i]})"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "Services (per server):"
|
||||||
|
echo " • Nextcloud: https://$SUBDOMAIN_NEXTCLOUD.<domain>"
|
||||||
|
echo " • OnlyOffice: https://$SUBDOMAIN_OFFICE.<domain>"
|
||||||
|
echo " • Excalidraw: https://$SUBDOMAIN_DRAW.<domain>"
|
||||||
|
echo " • Obsidian: https://$SUBDOMAIN_NOTES.<domain>"
|
||||||
|
echo " • Homarr: https://$SUBDOMAIN_HOMARR.<domain> (Tailscale)"
|
||||||
|
echo " • Dockhand: https://$SUBDOMAIN_DOCKHAND.<domain> (Tailscale)"
|
||||||
|
echo " • Uptime Kuma: https://$SUBDOMAIN_UPTIME.<domain> (Tailscale)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -p "Proceed with these settings? (y/n): " confirm
|
||||||
|
if [[ "$confirm" != "y" ]]; then
|
||||||
|
echo "Setup cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Generate Configuration Files
|
||||||
|
echo "Generating configuration files..."
|
||||||
|
|
||||||
|
# Create inventory directory
|
||||||
|
mkdir -p inventory/group_vars/all
|
||||||
|
|
||||||
|
# Generate hosts.yml
|
||||||
|
cat >inventory/hosts.yml <<EOF
|
||||||
|
# Ansible Inventory - Generated by setup.sh
|
||||||
|
# Date: $(date)
|
||||||
|
|
||||||
|
all:
|
||||||
|
children:
|
||||||
|
nextcloud_servers:
|
||||||
|
hosts:
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for i in "${!SERVER_IPS[@]}"; do
|
||||||
|
cat >>inventory/hosts.yml <<EOF
|
||||||
|
${SERVER_HOSTNAMES[$i]}:
|
||||||
|
ansible_host: ${SERVER_IPS[$i]}
|
||||||
|
ansible_user: ${SERVER_SSH_USERS[$i]}
|
||||||
|
ansible_ssh_private_key_file: ${SERVER_SSH_KEYS[$i]}
|
||||||
|
domain: ${SERVER_DOMAINS[$i]}
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Created: inventory/hosts.yml${NC}"
|
||||||
|
|
||||||
|
# Generate vars.yml (public variables)
|
||||||
|
cat >inventory/group_vars/all/vars.yml <<EOF
|
||||||
|
# Public Variables - Generated by setup.sh
|
||||||
|
# Date: $(date)
|
||||||
|
|
||||||
|
# User Information
|
||||||
|
user_name: "$USER_NAME"
|
||||||
|
user_email: "$USER_EMAIL"
|
||||||
|
timezone: "$TIMEZONE"
|
||||||
|
|
||||||
|
# Admin Account
|
||||||
|
admin_user: "$ADMIN_USER"
|
||||||
|
|
||||||
|
# Subdomain Configuration
|
||||||
|
subdomain_nextcloud: "$SUBDOMAIN_NEXTCLOUD"
|
||||||
|
subdomain_office: "$SUBDOMAIN_OFFICE"
|
||||||
|
subdomain_draw: "$SUBDOMAIN_DRAW"
|
||||||
|
subdomain_notes: "$SUBDOMAIN_NOTES"
|
||||||
|
subdomain_homarr: "$SUBDOMAIN_HOMARR"
|
||||||
|
subdomain_dockhand: "$SUBDOMAIN_DOCKHAND"
|
||||||
|
subdomain_uptime: "$SUBDOMAIN_UPTIME"
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
db_name: "$DB_NAME"
|
||||||
|
db_user: "$DB_USER"
|
||||||
|
|
||||||
|
# Backup Configuration
|
||||||
|
backup_retention_days: 30
|
||||||
|
install_rclone: $INSTALL_RCLONE
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
alert_email: "$ALERT_EMAIL"
|
||||||
|
enable_public_status: $ENABLE_PUBLIC_STATUS
|
||||||
|
|
||||||
|
# Deployment Settings
|
||||||
|
deployment_dir: "/opt/nextcloud-stack"
|
||||||
|
stack_name: "nextcloud"
|
||||||
|
|
||||||
|
# Docker Configuration
|
||||||
|
docker_compose_version: "v2"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vars.yml${NC}"
|
||||||
|
|
||||||
|
# Generate vault.yml (encrypted secrets)
|
||||||
|
VAULT_CONTENT=$(
|
||||||
|
cat <<EOF
|
||||||
|
# Encrypted Secrets - Generated by setup.sh
|
||||||
|
# Date: $(date)
|
||||||
|
|
||||||
|
# Admin Password
|
||||||
|
admin_password: "$ADMIN_PASSWORD"
|
||||||
|
|
||||||
|
# Database Credentials
|
||||||
|
db_password: "$DB_PASSWORD"
|
||||||
|
redis_password: "$REDIS_PASSWORD"
|
||||||
|
|
||||||
|
# Application Secrets
|
||||||
|
homarr_secret: "$HOMARR_SECRET"
|
||||||
|
|
||||||
|
# Tailscale
|
||||||
|
tailscale_auth_key: "$TAILSCALE_AUTH_KEY"
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save vault password to temporary file
|
||||||
|
echo "$VAULT_PASSWORD" >.vault_pass_temp
|
||||||
|
|
||||||
|
# Create encrypted vault
|
||||||
|
echo "$VAULT_CONTENT" | ansible-vault encrypt --vault-password-file=.vault_pass_temp --output=inventory/group_vars/all/vault.yml
|
||||||
|
|
||||||
|
# Clean up temp file
|
||||||
|
rm .vault_pass_temp
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vault.yml (encrypted)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Save vault password hint
|
||||||
|
echo "IMPORTANT: Save your vault password!"
|
||||||
|
echo "You will need it to run the playbook."
|
||||||
|
echo ""
|
||||||
|
echo "Vault password: **************** (hidden)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Final Instructions
|
||||||
|
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${CYAN}║ READY TO DEPLOY! ║${NC}"
|
||||||
|
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Review configuration (optional):"
|
||||||
|
echo " ansible-inventory --list"
|
||||||
|
echo ""
|
||||||
|
echo "2. Test connectivity:"
|
||||||
|
echo " ansible all -m ping --ask-vault-pass"
|
||||||
|
echo ""
|
||||||
|
echo "3. Deploy stack:"
|
||||||
|
echo " ansible-playbook playbooks/site.yml --ask-vault-pass"
|
||||||
|
echo ""
|
||||||
|
echo " OR use Makefile:"
|
||||||
|
echo " make deploy"
|
||||||
|
echo ""
|
||||||
|
echo "Estimated deployment time: 30-45 minutes per server"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}WARNING: Ensure DNS records are configured before deploying!${NC}"
|
||||||
|
echo " Let's Encrypt will fail if DNS is not pointing correctly."
|
||||||
|
echo ""
|
||||||
|
echo "Required DNS records for each server:"
|
||||||
|
for i in "${!SERVER_DOMAINS[@]}"; do
|
||||||
|
echo " ${SERVER_DOMAINS[$i]}:"
|
||||||
|
echo " $SUBDOMAIN_NEXTCLOUD.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_OFFICE.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_DRAW.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_NOTES.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_HOMARR.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_DOCKHAND.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo " $SUBDOMAIN_UPTIME.${SERVER_DOMAINS[$i]} → ${SERVER_IPS[$i]}"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "Setup complete! 🎉"
|
||||||
@@ -1,56 +1,47 @@
|
|||||||
## name: Rosé Pine Moon
|
background #282828
|
||||||
## author: mvllow
|
foreground #d4be98
|
||||||
## license: MIT
|
|
||||||
## upstream: https://github.com/rose-pine/kitty/blob/main/dist/rose-pine-moon.conf
|
|
||||||
## blurb: All natural pine, faux fur and a bit of soho vibes for the classy minimalist
|
|
||||||
|
|
||||||
foreground #e0def4
|
selection_background #d4be98
|
||||||
background #232136
|
selection_foreground #282828
|
||||||
selection_foreground #e0def4
|
|
||||||
selection_background #44415a
|
|
||||||
|
|
||||||
cursor #56526e
|
cursor #a89984
|
||||||
cursor_text_color #e0def4
|
cursor_text_color background
|
||||||
|
|
||||||
url_color #c4a7e7
|
active_tab_background #282828
|
||||||
|
active_tab_foreground #d4be98
|
||||||
|
active_tab_font_style bold
|
||||||
|
inactive_tab_background #282828
|
||||||
|
inactive_tab_foreground #a89984
|
||||||
|
inactive_tab_font_style normal
|
||||||
|
|
||||||
active_tab_foreground #e0def4
|
# Black
|
||||||
active_tab_background #393552
|
color0 #665c54
|
||||||
inactive_tab_foreground #6e6a86
|
color8 #928374
|
||||||
inactive_tab_background #232136
|
|
||||||
|
|
||||||
active_border_color #3e8fb0
|
# Red
|
||||||
inactive_border_color #44415a
|
color1 #ea6962
|
||||||
|
color9 #ea6962
|
||||||
|
|
||||||
# black
|
# Green
|
||||||
color0 #393552
|
color2 #a9b665
|
||||||
color8 #6e6a86
|
color10 #a9b665
|
||||||
|
|
||||||
# red
|
# Yellow
|
||||||
color1 #eb6f92
|
color3 #e78a4e
|
||||||
color9 #eb6f92
|
color11 #d8a657
|
||||||
|
|
||||||
# green
|
# Blue
|
||||||
color2 #3e8fb0
|
color4 #7daea3
|
||||||
color10 #3e8fb0
|
color12 #7daea3
|
||||||
|
|
||||||
# yellow
|
# Magenta
|
||||||
color3 #f6c177
|
color5 #d3869b
|
||||||
color11 #f6c177
|
color13 #d3869b
|
||||||
|
|
||||||
# blue
|
# Cyan
|
||||||
color4 #9ccfd8
|
color6 #89b482
|
||||||
color12 #9ccfd8
|
color14 #89b482
|
||||||
|
|
||||||
# magenta
|
|
||||||
color5 #c4a7e7
|
|
||||||
color13 #c4a7e7
|
|
||||||
|
|
||||||
# cyan
|
|
||||||
color6 #ea9a97
|
|
||||||
color14 #ea9a97
|
|
||||||
|
|
||||||
# white
|
|
||||||
color7 #e0def4
|
|
||||||
color15 #e0def4
|
|
||||||
|
|
||||||
|
# White
|
||||||
|
color7 #d4be98
|
||||||
|
color15 #d4be98
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BEGIN_KITTY_THEME
|
# BEGIN_KITTY_THEME
|
||||||
# Rosé Pine Moon
|
# Gruvbox Material
|
||||||
include current-theme.conf
|
include current-theme.conf
|
||||||
# END_KITTY_THEME
|
# END_KITTY_THEME
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BEGIN_KITTY_THEME
|
# BEGIN_KITTY_THEME
|
||||||
# Gruvbox Material
|
# Kanagawa
|
||||||
include current-theme.conf
|
include current-theme.conf
|
||||||
# END_KITTY_THEME
|
# END_KITTY_THEME
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ layout {
|
|||||||
}
|
}
|
||||||
focus-ring {
|
focus-ring {
|
||||||
width 2
|
width 2
|
||||||
active-color "#c4a7e7"
|
active-color "#7daea3"
|
||||||
inactive-color "#a6adc8"
|
inactive-color "#928374"
|
||||||
}
|
}
|
||||||
border {
|
border {
|
||||||
off
|
off
|
||||||
@@ -80,6 +80,7 @@ spawn-at-startup "swaync"
|
|||||||
// spawn-at-startup "kanshi"
|
// spawn-at-startup "kanshi"
|
||||||
spawn-at-startup "swww-daemon"
|
spawn-at-startup "swww-daemon"
|
||||||
spawn-at-startup "xwayland-satellite"
|
spawn-at-startup "xwayland-satellite"
|
||||||
|
spawn-at-startup "gammastep" "-l" "47.38:8.54" "-t" "6000:4000"
|
||||||
spawn-at-startup "kitty"
|
spawn-at-startup "kitty"
|
||||||
spawn-at-startup "librewolf"
|
spawn-at-startup "librewolf"
|
||||||
spawn-at-startup "sh" "-c" "sleep 5 && exec swayidle -w timeout 300 hyprlock timeout 600 'niri msg action power-off-monitors' resume 'niri msg action power-on-monitors' timeout 3600 'systemctl suspend'"
|
spawn-at-startup "sh" "-c" "sleep 5 && exec swayidle -w timeout 300 hyprlock timeout 600 'niri msg action power-off-monitors' resume 'niri msg action power-on-monitors' timeout 3600 'systemctl suspend'"
|
||||||
@@ -102,12 +103,23 @@ match app-id=r#"^org\.keepassxc\.KeePassXC$"#
|
|||||||
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
||||||
block-out-from "screen-capture"
|
block-out-from "screen-capture"
|
||||||
}
|
}
|
||||||
// terminal window-rule
|
|
||||||
window-rule {
|
window-rule {
|
||||||
match app-id="^(zoom|us.zoom.Zoom)$"
|
match app-id="Bitwarden"
|
||||||
exclude title="^.*Zoom Workplace"
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
open-floating true
|
open-floating true
|
||||||
}
|
}
|
||||||
|
// terminal window-rule
|
||||||
|
window-rule {
|
||||||
|
match app-id="Zoom Workplace"
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
// window-rule {
|
||||||
|
// match app-id="^(zoom|us.zoom.Zoom)$"
|
||||||
|
// exclude title="^.*Zoom Workplace"
|
||||||
|
// open-floating true
|
||||||
|
// }
|
||||||
|
|
||||||
window-rule {
|
window-rule {
|
||||||
match at-startup=true app-id="kitty"
|
match at-startup=true app-id="kitty"
|
||||||
@@ -173,6 +185,13 @@ window-rule {
|
|||||||
}
|
}
|
||||||
open-floating true
|
open-floating true
|
||||||
}
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_network-tui"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
window-rule {
|
window-rule {
|
||||||
geometry-corner-radius 5
|
geometry-corner-radius 5
|
||||||
clip-to-geometry true
|
clip-to-geometry true
|
||||||
@@ -221,6 +240,9 @@ binds {
|
|||||||
Mod+Mod5+T hotkey-overlay-title="Open Wavemon" {
|
Mod+Mod5+T hotkey-overlay-title="Open Wavemon" {
|
||||||
spawn "kitty" "--class" "floating_wavemon" "-e" "wavemon"
|
spawn "kitty" "--class" "floating_wavemon" "-e" "wavemon"
|
||||||
}
|
}
|
||||||
|
Mod+Mod5+Z hotkey-overlay-title="Open network-tui" {
|
||||||
|
spawn "kitty" "--class" "floating_network-tui" "-e" "network-tui"
|
||||||
|
}
|
||||||
// Brightness Controll
|
// Brightness Controll
|
||||||
Mod+Ctrl+0 {
|
Mod+Ctrl+0 {
|
||||||
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"
|
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ button:hover {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
#workspaces button {
|
#workspaces button {
|
||||||
padding: 0 10px;
|
padding: 0 2px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: @theme_fg_color;
|
color: @theme_fg_color;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
vim.cmd.colorscheme("rose-pine")
|
-- vim.cmd.colorscheme("rose-pine")
|
||||||
-- sonokai
|
-- vim.cmd.colorscheme("sonokai")
|
||||||
-- rose-pine
|
-- vim.cmd.colorscheme("one-dark")
|
||||||
-- onedark
|
-- vim.cmd.colorscheme("nightfox")
|
||||||
-- nightfox
|
-- vim.cmd.colorscheme("kanagawa")
|
||||||
-- kanagawa
|
vim.cmd.colorscheme("gruvbox-material")
|
||||||
-- gruvbox-mat
|
-- vim.cmd.colorscheme("everforest")
|
||||||
-- everforest
|
-- vim.cmd.colorscheme("dracula")
|
||||||
-- dracula
|
-- vim.cmd.colorscheme("carbonfox")
|
||||||
-- carbonfox
|
-- vim.cmd.colorscheme("tokyonight")
|
||||||
-- tokyonight
|
|
||||||
|
|||||||
@@ -6,13 +6,25 @@ return {
|
|||||||
main = "ibl",
|
main = "ibl",
|
||||||
event = { "BufReadPost", "BufNewFile" },
|
event = { "BufReadPost", "BufNewFile" },
|
||||||
config = function()
|
config = function()
|
||||||
|
-- Define highlights BEFORE setup() is called
|
||||||
|
local function set_highlights()
|
||||||
|
vim.api.nvim_set_hl(0, "IblIndent", { fg = "#313244" })
|
||||||
|
vim.api.nvim_set_hl(0, "IblScope", { fg = "#5E81AC" })
|
||||||
|
end
|
||||||
|
|
||||||
|
set_highlights()
|
||||||
|
vim.api.nvim_create_autocmd("ColorScheme", {
|
||||||
|
pattern = "*",
|
||||||
|
callback = set_highlights,
|
||||||
|
})
|
||||||
|
|
||||||
require("ibl").setup({
|
require("ibl").setup({
|
||||||
indent = {
|
indent = {
|
||||||
char = "▏",
|
char = "▏",
|
||||||
},
|
},
|
||||||
scope = {
|
scope = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
char = "▏", -- Same character, different color
|
char = "▏",
|
||||||
show_start = true,
|
show_start = true,
|
||||||
show_end = true,
|
show_end = true,
|
||||||
},
|
},
|
||||||
@@ -26,18 +38,6 @@ return {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Very subtle gray for all indent lines
|
|
||||||
vim.api.nvim_set_hl(0, "IblIndent", { fg = "#313244" })
|
|
||||||
|
|
||||||
-- Soft accent for current scope (choose one):
|
|
||||||
-- Blue:
|
|
||||||
vim.api.nvim_set_hl(0, "IblScope", { fg = "#5E81AC" })
|
|
||||||
|
|
||||||
-- Or Purple:
|
|
||||||
-- vim.api.nvim_set_hl(0, "IblScope", { fg = "#B48EAD" })
|
|
||||||
-- Or Green:
|
|
||||||
-- vim.api.nvim_set_hl(0, "IblScope", { fg = "#A3BE8C" })
|
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
/home/liph/.config/wofi/themes/rose-pine-moon.css
|
|
||||||
62
wofi/.config/wofi/style.css
Normal file
62
wofi/.config/wofi/style.css
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — Gruvbox Material
|
||||||
|
Based on sainnhe/gruvbox-material (Dark Hard variant)
|
||||||
|
Structure mirrored from provided rose-pine template
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
window {
|
||||||
|
margin: 0px;
|
||||||
|
background-color: #282828;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #7daea3;
|
||||||
|
color: #d4be98;
|
||||||
|
font-family: "Monofur Nerd Font";
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input {
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
color: #d8a657;
|
||||||
|
background-color: #32302f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
background-color: #32302f;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outer-box {
|
||||||
|
margin: 15px;
|
||||||
|
border: none;
|
||||||
|
background-color: #282828;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll {
|
||||||
|
margin: 0px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
color: #d4be98;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected {
|
||||||
|
background-color: #7daea3;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected * {
|
||||||
|
background-color: #7daea3;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
62
wofi/.config/wofi/themes/gruvbox-mat.css
Normal file
62
wofi/.config/wofi/themes/gruvbox-mat.css
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — Gruvbox Material
|
||||||
|
Based on sainnhe/gruvbox-material (Dark Hard variant)
|
||||||
|
Structure mirrored from provided rose-pine template
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
window {
|
||||||
|
margin: 0px;
|
||||||
|
background-color: #282828;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #7daea3;
|
||||||
|
color: #d4be98;
|
||||||
|
font-family: "Monofur Nerd Font";
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input {
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
color: #d8a657;
|
||||||
|
background-color: #32302f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
background-color: #32302f;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outer-box {
|
||||||
|
margin: 15px;
|
||||||
|
border: none;
|
||||||
|
background-color: #282828;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll {
|
||||||
|
margin: 0px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
color: #d4be98;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected {
|
||||||
|
background-color: #7daea3;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected * {
|
||||||
|
background-color: #7daea3;
|
||||||
|
color: #282828;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
125
wofi/.config/wofi/themes/kanagawa.css
Normal file
125
wofi/.config/wofi/themes/kanagawa.css
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — Kanagawa
|
||||||
|
Based on rebelot/kanagawa.nvim (Wave variant)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "JetBrains Mono", "Iosevka", monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
transition:
|
||||||
|
background-color 0.1s ease,
|
||||||
|
color 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main window ── */
|
||||||
|
window {
|
||||||
|
background-color: #1f1f28;
|
||||||
|
border: 1px solid #2a2a37;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Outer layout wrapper ── */
|
||||||
|
#outer-box {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Search input wrapper ── */
|
||||||
|
#input {
|
||||||
|
background-color: #16161d;
|
||||||
|
color: #dcd7ba;
|
||||||
|
border: 1px solid #363646;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
caret-color: #e82424;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input:focus {
|
||||||
|
border-color: #7e9cd8;
|
||||||
|
box-shadow: 0 0 0 2px rgba(126, 156, 216, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#input image {
|
||||||
|
color: #54546d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Scrollable results area ── */
|
||||||
|
#scroll {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Individual entry row ── */
|
||||||
|
#entry {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #dcd7ba;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
margin: 2px 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:hover {
|
||||||
|
background-color: #2a2a37;
|
||||||
|
border-color: #363646;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected,
|
||||||
|
#entry.selected {
|
||||||
|
background-color: #223249;
|
||||||
|
border-color: #7e9cd8;
|
||||||
|
color: #7e9cd8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry image / icon ── */
|
||||||
|
#entry image {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #98bb6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected image,
|
||||||
|
#entry.selected image {
|
||||||
|
color: #7e9cd8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry text label ── */
|
||||||
|
#entry label {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Kanagawa accent palette ── */
|
||||||
|
/* wave-red e82424 */
|
||||||
|
/* autumn-yel e6c384 */
|
||||||
|
/* spring-grn 98bb6c */
|
||||||
|
/* crystal-blu 7e9cd8 */
|
||||||
|
/* oni-violet 957fb8 */
|
||||||
|
/* dragon-org ffa066 */
|
||||||
|
/* spring-vio 938aa9 */
|
||||||
|
|
||||||
|
/* ── Scrollbar ── */
|
||||||
|
scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider {
|
||||||
|
background-color: #363646;
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider:hover {
|
||||||
|
background-color: #7e9cd8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── No results message ── */
|
||||||
|
#no-results {
|
||||||
|
color: #54546d;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
126
wofi/.config/wofi/themes/nightfox.css
Normal file
126
wofi/.config/wofi/themes/nightfox.css
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — Nightfox
|
||||||
|
Based on EdenEast/nightfox.nvim (Nightfox variant)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "JetBrains Mono", "Iosevka", monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
transition:
|
||||||
|
background-color 0.1s ease,
|
||||||
|
color 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main window ── */
|
||||||
|
window {
|
||||||
|
background-color: #192330;
|
||||||
|
border: 1px solid #212e3f;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Outer layout wrapper ── */
|
||||||
|
#outer-box {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Search input ── */
|
||||||
|
#input {
|
||||||
|
background-color: #131a24;
|
||||||
|
color: #cdcecf;
|
||||||
|
border: 1px solid #29394f;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
caret-color: #c94f6d;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input:focus {
|
||||||
|
border-color: #719cd6;
|
||||||
|
box-shadow: 0 0 0 2px rgba(113, 156, 214, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#input image {
|
||||||
|
color: #3b4261;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Scrollable results area ── */
|
||||||
|
#scroll {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Individual entry row ── */
|
||||||
|
#entry {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #cdcecf;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
margin: 2px 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:hover {
|
||||||
|
background-color: #212e3f;
|
||||||
|
border-color: #29394f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected,
|
||||||
|
#entry.selected {
|
||||||
|
background-color: #1d3347;
|
||||||
|
border-color: #719cd6;
|
||||||
|
color: #719cd6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry image / icon ── */
|
||||||
|
#entry image {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #81b29a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected image,
|
||||||
|
#entry.selected image {
|
||||||
|
color: #719cd6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry text label ── */
|
||||||
|
#entry label {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Nightfox accent palette ── */
|
||||||
|
/* red c94f6d */
|
||||||
|
/* orange f4a261 */
|
||||||
|
/* yellow dbc074 */
|
||||||
|
/* green 81b29a */
|
||||||
|
/* cyan 63cdcf */
|
||||||
|
/* blue 719cd6 */
|
||||||
|
/* magenta 9d79d6 */
|
||||||
|
/* pink d67ad2 */
|
||||||
|
|
||||||
|
/* ── Scrollbar ── */
|
||||||
|
scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider {
|
||||||
|
background-color: #29394f;
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider:hover {
|
||||||
|
background-color: #719cd6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── No results message ── */
|
||||||
|
#no-results {
|
||||||
|
color: #3b4261;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
125
wofi/.config/wofi/themes/one-dark.css
Normal file
125
wofi/.config/wofi/themes/one-dark.css
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — One Dark
|
||||||
|
Based on Atom's One Dark (navarasu/onedark.nvim)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "JetBrains Mono", "Iosevka", monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
transition:
|
||||||
|
background-color 0.1s ease,
|
||||||
|
color 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main window ── */
|
||||||
|
window {
|
||||||
|
background-color: #282c34;
|
||||||
|
border: 1px solid #3e4451;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Outer layout wrapper ── */
|
||||||
|
#outer-box {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Search input ── */
|
||||||
|
#input {
|
||||||
|
background-color: #21252b;
|
||||||
|
color: #abb2bf;
|
||||||
|
border: 1px solid #4b5263;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
caret-color: #e06c75;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input:focus {
|
||||||
|
border-color: #61afef;
|
||||||
|
box-shadow: 0 0 0 2px rgba(97, 175, 239, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#input image {
|
||||||
|
color: #5c6370;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Scrollable results area ── */
|
||||||
|
#scroll {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Individual entry row ── */
|
||||||
|
#entry {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #abb2bf;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
margin: 2px 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:hover {
|
||||||
|
background-color: #2c313a;
|
||||||
|
border-color: #3e4451;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected,
|
||||||
|
#entry.selected {
|
||||||
|
background-color: #2d3750;
|
||||||
|
border-color: #61afef;
|
||||||
|
color: #61afef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry image / icon ── */
|
||||||
|
#entry image {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #98c379;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected image,
|
||||||
|
#entry.selected image {
|
||||||
|
color: #61afef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry text label ── */
|
||||||
|
#entry label {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── One Dark accent palette ── */
|
||||||
|
/* red e06c75 */
|
||||||
|
/* orange d19a66 */
|
||||||
|
/* yellow e5c07b */
|
||||||
|
/* green 98c379 */
|
||||||
|
/* cyan 56b6c2 */
|
||||||
|
/* blue 61afef */
|
||||||
|
/* magenta c678dd */
|
||||||
|
|
||||||
|
/* ── Scrollbar ── */
|
||||||
|
scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider {
|
||||||
|
background-color: #4b5263;
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider:hover {
|
||||||
|
background-color: #61afef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── No results message ── */
|
||||||
|
#no-results {
|
||||||
|
color: #5c6370;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
125
wofi/.config/wofi/themes/sonokai.css
Normal file
125
wofi/.config/wofi/themes/sonokai.css
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/* ============================================================
|
||||||
|
Wofi Theme — Sonokai
|
||||||
|
Based on Sainnhe's Sonokai (Shusia variant)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "JetBrains Mono", "Iosevka", monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
transition:
|
||||||
|
background-color 0.1s ease,
|
||||||
|
color 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main window ── */
|
||||||
|
window {
|
||||||
|
background-color: #2d2a2e;
|
||||||
|
border: 1px solid #403e41;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Outer layout wrapper ── */
|
||||||
|
#outer-box {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Search input wrapper ── */
|
||||||
|
#input {
|
||||||
|
background-color: #221f22;
|
||||||
|
color: #e3e1e4;
|
||||||
|
border: 1px solid #5b595c;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
caret-color: #ff6188;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input:focus {
|
||||||
|
border-color: #ff6188;
|
||||||
|
box-shadow: 0 0 0 2px rgba(255, 97, 136, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#input image {
|
||||||
|
/* search icon */
|
||||||
|
color: #727072;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Scrollable results area ── */
|
||||||
|
#scroll {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Individual entry row ── */
|
||||||
|
#entry {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #e3e1e4;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
margin: 2px 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:hover {
|
||||||
|
background-color: #3a3a3c;
|
||||||
|
border-color: #5b595c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected,
|
||||||
|
#entry.selected {
|
||||||
|
background-color: #403e41;
|
||||||
|
border-color: #ff6188;
|
||||||
|
color: #ff6188;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry image / icon ── */
|
||||||
|
#entry image {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #a9dc76;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected image,
|
||||||
|
#entry.selected image {
|
||||||
|
color: #ff6188;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Entry text label ── */
|
||||||
|
#entry label {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Custom property colours (usable in sub-text) ── */
|
||||||
|
/* accent-1 ff6188 pink */
|
||||||
|
/* accent-2 fc9867 orange */
|
||||||
|
/* accent-3 ffd866 yellow */
|
||||||
|
/* accent-4 a9dc76 green */
|
||||||
|
/* accent-5 78dce8 cyan */
|
||||||
|
/* accent-6 ab9df2 purple */
|
||||||
|
|
||||||
|
/* ── Scrollbar ── */
|
||||||
|
scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider {
|
||||||
|
background-color: #5b595c;
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar slider:hover {
|
||||||
|
background-color: #ff6188;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── No results message ── */
|
||||||
|
#no-results {
|
||||||
|
color: #727072;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
30
wofi/.config/wofi/themes/tokyo-night.css
Normal file
30
wofi/.config/wofi/themes/tokyo-night.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
window {
|
||||||
|
background-color: rgb(26, 27, 38);
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input {
|
||||||
|
padding: 0.7em;
|
||||||
|
border-radius: 0.5em 0.5em 0 0;
|
||||||
|
background-color: #2d2e40;
|
||||||
|
color: #c0caf5;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text {
|
||||||
|
color: #c0caf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry {
|
||||||
|
padding: 0.7em;
|
||||||
|
color: #c0caf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected {
|
||||||
|
background-color: #7aa2f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text:selected {
|
||||||
|
color: #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
1
yazi/.config/yazi/flavors/dracula.yazi
Submodule
1
yazi/.config/yazi/flavors/dracula.yazi
Submodule
Submodule yazi/.config/yazi/flavors/dracula.yazi added at 1b1a8d1083
21
yazi/.config/yazi/flavors/everforest-medium.yazi/LICENSE
Normal file
21
yazi/.config/yazi/flavors/everforest-medium.yazi/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Chromium Oxide
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Mitchell Hanberg
|
||||||
|
|
||||||
|
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.
|
||||||
45
yazi/.config/yazi/flavors/everforest-medium.yazi/README.md
Normal file
45
yazi/.config/yazi/flavors/everforest-medium.yazi/README.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
Example Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
## 👀 Preview
|
||||||
|
|
||||||
|
<img src="preview.png" width="600" />
|
||||||
|
|
||||||
|
## 🎨 Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ya pkg add Chromium-3-Oxide/everforest-medium
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ya pack -a Chromium-3-Oxide/everforest-medium
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Usage
|
||||||
|
|
||||||
|
Add the these lines to your `theme.toml` configuration file to use it:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
dark = "everforest-medium"
|
||||||
|
```
|
||||||
|
|
||||||
|
For Yazi versions before 0.4:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
use = "everforest-medium"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
|
||||||
|
|
||||||
|
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.
|
||||||
229
yazi/.config/yazi/flavors/everforest-medium.yazi/flavor.toml
Normal file
229
yazi/.config/yazi/flavors/everforest-medium.yazi/flavor.toml
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
# : Manager {{{
|
||||||
|
|
||||||
|
[mgr]
|
||||||
|
cwd = { fg = "#7fbbb3" }
|
||||||
|
|
||||||
|
# Hovered
|
||||||
|
hovered = { reversed = true }
|
||||||
|
preview_hovered = { underline = true }
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find_keyword = { fg = "#dbbc7f", bold = true, italic = true, underline = true }
|
||||||
|
find_position = { fg = "#d699b6", bg = "reset", bold = true, italic = true }
|
||||||
|
|
||||||
|
# Symlink
|
||||||
|
symlink_target = { italic = true }
|
||||||
|
|
||||||
|
# Marker
|
||||||
|
marker_copied = { fg = "#a7c080", bg = "#a7c080" }
|
||||||
|
marker_cut = { fg = "#e67e80", bg = "#e67e80" }
|
||||||
|
marker_marked = { fg = "#7fbbb3", bg = "#7fbbb3" }
|
||||||
|
marker_selected = { fg = "#dbbc7f", bg = "#dbbc7f" }
|
||||||
|
|
||||||
|
# Count
|
||||||
|
count_copied = { fg = "#343f44", bg = "#a7c080" }
|
||||||
|
count_cut = { fg = "#343f44", bg = "#e67e80" }
|
||||||
|
count_selected = { fg = "#343f44", bg = "#dbbc7f" }
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border_symbol = "│"
|
||||||
|
border_style = { fg = "#4f585e" }
|
||||||
|
|
||||||
|
# Highlighting
|
||||||
|
syntect_theme = ""
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Tabs {{{
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
active = { bg = "#7fbbb3", bold = true }
|
||||||
|
inactive = { fg = "#7fbbb3", bg = "#4f585e" }
|
||||||
|
|
||||||
|
# Separator
|
||||||
|
sep_inner = { open = "", close = "" }
|
||||||
|
sep_outer = { open = "", close = "" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Mode {{{
|
||||||
|
|
||||||
|
[mode]
|
||||||
|
normal_main = { fg = "#3d484d", bg = "#a7c080", bold = true }
|
||||||
|
normal_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
|
||||||
|
|
||||||
|
# Select mode
|
||||||
|
select_main = { fg = "#3d484d", bg = "#e67e80", bold = true }
|
||||||
|
select_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
|
||||||
|
|
||||||
|
# Unset mode
|
||||||
|
unset_main = { fg = "#3d484d", bg = "#7fbbb3", bold = true }
|
||||||
|
unset_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Status bar {{{
|
||||||
|
|
||||||
|
[status]
|
||||||
|
overall = {}
|
||||||
|
sep_left = { open = "", close = "" }
|
||||||
|
sep_right = { open = "", close = "" }
|
||||||
|
|
||||||
|
# Permissions
|
||||||
|
permissions_s = { fg = "#2d353b" }
|
||||||
|
permissions_t = { fg = "#a7c080" }
|
||||||
|
permissions_r = { fg = "#dbbc7f" }
|
||||||
|
permissions_w = { fg = "#e67e80" }
|
||||||
|
permissions_x = { fg = "#7fbbb3" }
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
progress_label = { bold = true }
|
||||||
|
progress_normal = { fg = "#7fbbb3", bg = "#232a2e" }
|
||||||
|
progress_error = { fg = "#e67e80", bg = "#232a2e" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Which {{{
|
||||||
|
|
||||||
|
[which]
|
||||||
|
cols = 3
|
||||||
|
mask = { bg = "#2d353b" }
|
||||||
|
cand = { fg = "#7fbbb3" }
|
||||||
|
rest = { fg = "#2d353b" }
|
||||||
|
desc = { fg = "#d699b6" }
|
||||||
|
separator = " "
|
||||||
|
separator_style = { fg = "#2d353b" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Confirmation {{{
|
||||||
|
|
||||||
|
[confirm]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
title = { fg = "#7fbbb3" }
|
||||||
|
body = {}
|
||||||
|
list = {}
|
||||||
|
btn_yes = { reversed = true }
|
||||||
|
btn_no = {}
|
||||||
|
btn_labels = [ " [Y]es ", " (N)o " ]
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Spotter {{{
|
||||||
|
|
||||||
|
[spot]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
title = { fg = "#7fbbb3" }
|
||||||
|
|
||||||
|
# Table
|
||||||
|
tbl_col = { fg = "#7fbbb3" }
|
||||||
|
tbl_cell = { fg = "#dbbc7f", reversed = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Notification {{{
|
||||||
|
|
||||||
|
[notify]
|
||||||
|
title_info = { fg = "#a7c080" }
|
||||||
|
title_warn = { fg = "#dbbc7f" }
|
||||||
|
title_error = { fg = "#e67e80" }
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
icon_info = ""
|
||||||
|
icon_warn = ""
|
||||||
|
icon_error = ""
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Picker {{{
|
||||||
|
|
||||||
|
[pick]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
active = { fg = "#d699b6", bold = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Input {{{
|
||||||
|
|
||||||
|
[input]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
title = {}
|
||||||
|
value = {}
|
||||||
|
selected = { reversed = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Completion {{{
|
||||||
|
|
||||||
|
[cmp]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
active = { reversed = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
icon_file = ""
|
||||||
|
icon_folder = ""
|
||||||
|
icon_command = ""
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Task manager {{{
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
border = { fg = "#7fbbb3" }
|
||||||
|
title = {}
|
||||||
|
hovered = { fg = "#d699b6", underline = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Help menu {{{
|
||||||
|
|
||||||
|
[help]
|
||||||
|
on = { fg = "#7fbbb3" }
|
||||||
|
run = { fg = "#d699b6" }
|
||||||
|
desc = {}
|
||||||
|
hovered = { reversed = true, bold = true }
|
||||||
|
footer = { fg = "#2d353b", bg = "#d3c6aa" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : File-specific styles {{{
|
||||||
|
|
||||||
|
[filetype]
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
# Images
|
||||||
|
{ mime = "image/*", fg = "#7fbbb3" },
|
||||||
|
|
||||||
|
# Media
|
||||||
|
{ mime = "{audio,video}/*", fg = "#d699b6" },
|
||||||
|
|
||||||
|
# Archives
|
||||||
|
{ mime = "application/*zip", fg = "#e67e80" },
|
||||||
|
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "#e67e80" },
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "#7fbbb3" },
|
||||||
|
|
||||||
|
# Fallback
|
||||||
|
{ name = "*", fg = "#83c092" },
|
||||||
|
{ name = "*/", fg = "#a7c080" }
|
||||||
|
]
|
||||||
|
|
||||||
|
# : }}}
|
||||||
BIN
yazi/.config/yazi/flavors/everforest-medium.yazi/preview.png
Normal file
BIN
yazi/.config/yazi/flavors/everforest-medium.yazi/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
3186
yazi/.config/yazi/flavors/everforest-medium.yazi/tmtheme.xml
Normal file
3186
yazi/.config/yazi/flavors/everforest-medium.yazi/tmtheme.xml
Normal file
File diff suppressed because it is too large
Load Diff
21
yazi/.config/yazi/flavors/gruvbox-material.yazi/LICENSE
Normal file
21
yazi/.config/yazi/flavors/gruvbox-material.yazi/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 - Matthew Dong
|
||||||
|
|
||||||
|
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.
|
||||||
201
yazi/.config/yazi/flavors/gruvbox-material.yazi/LICENSE-tmtheme
Normal file
201
yazi/.config/yazi/flavors/gruvbox-material.yazi/LICENSE-tmtheme
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
42
yazi/.config/yazi/flavors/gruvbox-material.yazi/README.md
Normal file
42
yazi/.config/yazi/flavors/gruvbox-material.yazi/README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
Gruvbox Material Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
## 👀 Preview
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 🎨 Installation
|
||||||
|
|
||||||
|
### Using package manager
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ya pkg add matt-dong-123/gruvbox-material
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
git clone https://github.com/your-username/gruvbox-material.yazi.git ~/.config/yazi/flavors/gruvbox-material.yazi
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
git clone https://github.com/your-username/gruvbox-material.yazi.git %AppData%\yazi\config\flavors\gruvbox-material.yazi
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Usage
|
||||||
|
|
||||||
|
Add the these lines to your `theme.toml` configuration file to use it:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
dark = "gruvbox-material"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
Check the [LICENSE](LICENSE) file for more details.
|
||||||
214
yazi/.config/yazi/flavors/gruvbox-material.yazi/flavor.toml
Normal file
214
yazi/.config/yazi/flavors/gruvbox-material.yazi/flavor.toml
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
# : Manager {{{
|
||||||
|
|
||||||
|
[mgr]
|
||||||
|
cwd = { fg = "#89b482" }
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find_keyword = { fg = "#d8a657", bold = true, italic = true, underline = true }
|
||||||
|
find_position = { fg = "#d3869b", bg = "reset", bold = true, italic = true }
|
||||||
|
|
||||||
|
# Marker
|
||||||
|
marker_copied = { fg = "#a9b665", bg = "#a9b665" }
|
||||||
|
marker_cut = { fg = "#ea6962", bg = "#ea6962" }
|
||||||
|
marker_marked = { fg = "#89b482", bg = "#89b482" }
|
||||||
|
marker_selected = { fg = "#d8a657", bg = "#d8a657" }
|
||||||
|
|
||||||
|
# Count
|
||||||
|
count_copied = { fg = "#282828", bg = "#a9b665" }
|
||||||
|
count_cut = { fg = "#282828", bg = "#ea6962" }
|
||||||
|
count_selected = { fg = "#282828", bg = "#d8a657" }
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border_symbol = "│"
|
||||||
|
border_style = { fg = "#928374" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Tabs {{{
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
active = { fg = "#282828", bg = "#7daea3", bold = true }
|
||||||
|
inactive = { fg = "#7daea3", bg = "#32302f" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Mode {{{
|
||||||
|
|
||||||
|
[mode]
|
||||||
|
|
||||||
|
normal_main = { fg = "#282828", bg = "#7daea3", bold = true }
|
||||||
|
normal_alt = { fg = "#7daea3", bg = "#32302f" }
|
||||||
|
|
||||||
|
# Select mode
|
||||||
|
select_main = { fg = "#282828", bg = "#89b482", bold = true }
|
||||||
|
select_alt = { fg = "#89b482", bg = "#32302f" }
|
||||||
|
|
||||||
|
# Unset mode
|
||||||
|
unset_main = { fg = "#282828", bg = "#ea6962", bold = true }
|
||||||
|
unset_alt = { fg = "#ea6962", bg = "#32302f" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Status bar {{{
|
||||||
|
|
||||||
|
[status]
|
||||||
|
# Permissions
|
||||||
|
perm_sep = { fg = "#928374" }
|
||||||
|
perm_type = { fg = "#7daea3" }
|
||||||
|
perm_read = { fg = "#d8a657" }
|
||||||
|
perm_write = { fg = "#ea6962" }
|
||||||
|
perm_exec = { fg = "#a9b665" }
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
progress_label = { fg = "#ddc7a1", bold = true }
|
||||||
|
progress_normal = { fg = "#a9b665", bg = "#45403d" }
|
||||||
|
progress_error = { fg = "#d8a657", bg = "#ea6962" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Pick {{{
|
||||||
|
|
||||||
|
[pick]
|
||||||
|
border = { fg = "#7daea3" }
|
||||||
|
active = { fg = "#d3869b", bold = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Input {{{
|
||||||
|
|
||||||
|
[input]
|
||||||
|
border = { fg = "#7daea3" }
|
||||||
|
title = {}
|
||||||
|
value = {}
|
||||||
|
selected = { reversed = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Completion {{{
|
||||||
|
|
||||||
|
[cmp]
|
||||||
|
border = { fg = "#7daea3" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Tasks {{{
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
border = { fg = "#7daea3" }
|
||||||
|
title = {}
|
||||||
|
hovered = { fg = "#d3869b", bold = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Which {{{
|
||||||
|
|
||||||
|
[which]
|
||||||
|
mask = { bg = "#32302f" }
|
||||||
|
cand = { fg = "#89b482" }
|
||||||
|
rest = { fg = "#a89984" }
|
||||||
|
desc = { fg = "#d3869b" }
|
||||||
|
separator = " "
|
||||||
|
separator_style = { fg = "#7c6f64" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Help {{{
|
||||||
|
|
||||||
|
[help]
|
||||||
|
on = { fg = "#89b482" }
|
||||||
|
run = { fg = "#d3869b" }
|
||||||
|
hovered = { reversed = true, bold = true }
|
||||||
|
footer = { fg = "#32302f", bg = "#d4be98" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Spotter {{{
|
||||||
|
|
||||||
|
[spot]
|
||||||
|
border = { fg = "#7daea3" }
|
||||||
|
title = { fg = "#7daea3" }
|
||||||
|
tbl_col = { fg = "#89b482" }
|
||||||
|
tbl_cell = { fg = "#d3869b", bg = "#45403d" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Notification {{{
|
||||||
|
|
||||||
|
[notify]
|
||||||
|
title_info = { fg = "#a9b665" }
|
||||||
|
title_warn = { fg = "#d8a657" }
|
||||||
|
title_error = { fg = "#ea6962" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : File-specific styles {{{
|
||||||
|
|
||||||
|
[filetype]
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
# Image
|
||||||
|
{ mime = "image/*", fg = "#89b482" },
|
||||||
|
# Media
|
||||||
|
{ mime = "{audio,video}/*", fg = "#d8a657" },
|
||||||
|
# Archive
|
||||||
|
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#d3869b" },
|
||||||
|
# Document
|
||||||
|
{ mime = "application/{pdf,doc,rtf}", fg = "#a9b665" },
|
||||||
|
# Virtual file system
|
||||||
|
{ mime = "vfs/{absent,stale}", fg = "#a89984" },
|
||||||
|
# Fallback
|
||||||
|
{ url = "*", fg = "#d4be98" },
|
||||||
|
{ url = "*/", fg = "#7daea3" },
|
||||||
|
]
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
[icon]
|
||||||
|
|
||||||
|
dirs = [
|
||||||
|
{ name = ".config", text = "", fg = "#d3869b" },
|
||||||
|
{ name = ".git", text = "", fg = "#89b482" },
|
||||||
|
{ name = ".github", text = "", fg = "#7daea3" },
|
||||||
|
{ name = ".npm", text = "", fg = "#7daea3" },
|
||||||
|
{ name = "Desktop", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Development", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Documents", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Downloads", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Library", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Movies", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Music", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Pictures", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Public", text = "", fg = "#89b482" },
|
||||||
|
{ name = "Videos", text = "", fg = "#89b482" },
|
||||||
|
]
|
||||||
|
conds = [
|
||||||
|
# Special files
|
||||||
|
{ if = "orphan", text = "", fg = "#d4be98" },
|
||||||
|
{ if = "link", text = "", fg = "#928374" },
|
||||||
|
{ if = "block", text = "", fg = "#ea6962" },
|
||||||
|
{ if = "char", text = "", fg = "#ea6962" },
|
||||||
|
{ if = "fifo", text = "", fg = "#ea6962" },
|
||||||
|
{ if = "sock", text = "", fg = "#ea6962" },
|
||||||
|
{ if = "sticky", text = "", fg = "#ea6962" },
|
||||||
|
{ if = "dummy", text = "", fg = "#ea6962" },
|
||||||
|
|
||||||
|
# Fallback
|
||||||
|
{ if = "dir", text = "", fg = "#7daea3" },
|
||||||
|
{ if = "exec", text = "", fg = "#a9b665" },
|
||||||
|
{ if = "!dir", text = "", fg = "#d4be98" },
|
||||||
|
]
|
||||||
BIN
yazi/.config/yazi/flavors/gruvbox-material.yazi/preview.png
Normal file
BIN
yazi/.config/yazi/flavors/gruvbox-material.yazi/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 226 KiB |
1376
yazi/.config/yazi/flavors/gruvbox-material.yazi/tmtheme.xml
Normal file
1376
yazi/.config/yazi/flavors/gruvbox-material.yazi/tmtheme.xml
Normal file
File diff suppressed because it is too large
Load Diff
21
yazi/.config/yazi/flavors/kanagawa.yazi/LICENSE
Normal file
21
yazi/.config/yazi/flavors/kanagawa.yazi/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Dmitry Nefedov
|
||||||
|
|
||||||
|
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.
|
||||||
21
yazi/.config/yazi/flavors/kanagawa.yazi/LICENSE-tmtheme
Normal file
21
yazi/.config/yazi/flavors/kanagawa.yazi/LICENSE-tmtheme
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Dmitry Nefedov
|
||||||
|
|
||||||
|
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.
|
||||||
36
yazi/.config/yazi/flavors/kanagawa.yazi/README.md
Normal file
36
yazi/.config/yazi/flavors/kanagawa.yazi/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
Kanagawa Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
## 👀 Preview
|
||||||
|
|
||||||
|
<img src="preview.png" width="600" />
|
||||||
|
|
||||||
|
## 🎨 Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ya pkg add dangooddd/kanagawa
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Usage
|
||||||
|
|
||||||
|
> [!Note]
|
||||||
|
> To achieve the look like in the screenshot you also need to install
|
||||||
|
> [full-border](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi) plugin!
|
||||||
|
|
||||||
|
Add these lines to your `theme.toml` configuration file to use it:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
dark = "kanagawa"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
|
||||||
|
|
||||||
|
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.
|
||||||
143
yazi/.config/yazi/flavors/kanagawa.yazi/flavor.toml
Normal file
143
yazi/.config/yazi/flavors/kanagawa.yazi/flavor.toml
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
## Color palette
|
||||||
|
## Check https://github.com/rebelot/kanagawa.nvim for more details
|
||||||
|
# sumiInk0 "#16161d" dark bg
|
||||||
|
# sumiInk3 "#1f1f28" bg
|
||||||
|
# sumiInk4 "#2a2a37"
|
||||||
|
# sumiInk6 "#54546d"
|
||||||
|
# fujiWhite "#dcd7ba" fg
|
||||||
|
# oldWhite "#c8c093" dark fg
|
||||||
|
# fujiGray "#727169"
|
||||||
|
# peachRed "#ff5d62"
|
||||||
|
# autumnRed "#c34043"
|
||||||
|
# crystalBlue "#7e9cd8"
|
||||||
|
# waveRed "#e46876"
|
||||||
|
# carpYellow "#e6c384"
|
||||||
|
# springGreen "#98bb6c"
|
||||||
|
# autumnGreen "#76946a"
|
||||||
|
# oniViolet "#957fb8"
|
||||||
|
# waveAqua1 "#6a9589"
|
||||||
|
# surimiOrange "#ffa066"
|
||||||
|
# waveAqua2 "#7aa89f"
|
||||||
|
|
||||||
|
[mgr]
|
||||||
|
marker_copied = { fg = "#98bb6c", bg = "#98bb6c" }
|
||||||
|
marker_cut = { fg = "#e46876", bg = "#e46876" }
|
||||||
|
marker_marked = { fg = "#957fb8", bg = "#957fb8" }
|
||||||
|
marker_selected = { fg = "#ffa066", bg = "#ffa066" }
|
||||||
|
|
||||||
|
cwd = { fg = "#e6c384" }
|
||||||
|
|
||||||
|
find_keyword = { fg = "#ffa066", bg = "#1f1f28" }
|
||||||
|
find_position = {}
|
||||||
|
|
||||||
|
count_copied = { fg = "#1f1f28", bg = "#98bb6c" }
|
||||||
|
count_cut = { fg = "#1f1f28", bg = "#e46876" }
|
||||||
|
count_selected = { fg = "#1f1f28", bg = "#ffa066" }
|
||||||
|
|
||||||
|
border_symbol = "│"
|
||||||
|
border_style = { fg = "#dcd7ba" }
|
||||||
|
|
||||||
|
[indicator]
|
||||||
|
parent = { reversed = true }
|
||||||
|
current = { reversed = true }
|
||||||
|
preview = { reversed = true }
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
active = { fg = "#1f1f28", bg = "#7e9cd8" }
|
||||||
|
inactive = { fg = "#7e9cd8", bg = "#2a2a37" }
|
||||||
|
|
||||||
|
sep_inner = { open = "", close = "" }
|
||||||
|
sep_outer = { open = "", close = "" }
|
||||||
|
|
||||||
|
|
||||||
|
[mode]
|
||||||
|
normal_main = { fg = "#1f1f28", bg = "#7e9cd8" }
|
||||||
|
normal_alt = { fg = "#7e9cd8", bg = "#2a2a37" }
|
||||||
|
select_main = { fg = "#1f1f28", bg = "#957fb8" }
|
||||||
|
select_alt = { fg = "#957fb8", bg = "#2a2a37" }
|
||||||
|
unset_main = { fg = "#1f1f28", bg = "#e6c384" }
|
||||||
|
unset_alt = { fg = "#e6c384", bg = "#2a2a37" }
|
||||||
|
|
||||||
|
|
||||||
|
[status]
|
||||||
|
sep_left = { open = "", close = "" }
|
||||||
|
sep_right = { open = "", close = "" }
|
||||||
|
overall = { fg = "#c8c093", bg = "#16161d" }
|
||||||
|
|
||||||
|
progress_label = { fg = "#7e9cd8", bold = true }
|
||||||
|
progress_normal = { fg = "#2a2a37", bg = "#1f1f28" }
|
||||||
|
progress_error = { fg = "#2a2a37", bg = "#1f1f28" }
|
||||||
|
|
||||||
|
perm_type = { fg = "#98bb6c" }
|
||||||
|
perm_read = { fg = "#e6c384" }
|
||||||
|
perm_write = { fg = "#ff5d62" }
|
||||||
|
perm_exec = { fg = "#7aa89f" }
|
||||||
|
perm_sep = { fg = "#957fb8" }
|
||||||
|
|
||||||
|
|
||||||
|
[pick]
|
||||||
|
border = { fg = "#7fb4ca" }
|
||||||
|
active = { fg = "#957fb8", bold = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
[input]
|
||||||
|
border = { fg = "#7fb4ca" }
|
||||||
|
title = {}
|
||||||
|
value = {}
|
||||||
|
selected = { reversed = true }
|
||||||
|
|
||||||
|
[completion]
|
||||||
|
border = { fg = "#7fb4ca" }
|
||||||
|
active = { reversed = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
border = { fg = "#7fb4ca" }
|
||||||
|
title = {}
|
||||||
|
hovered = { fg = "#957fb8" }
|
||||||
|
|
||||||
|
[which]
|
||||||
|
cols = 2
|
||||||
|
separator = " - "
|
||||||
|
separator_style = { fg = "#727169" }
|
||||||
|
mask = { bg = "#16161d" }
|
||||||
|
rest = { fg = "#727169" }
|
||||||
|
cand = { fg = "#7e9cd8" }
|
||||||
|
desc = { fg = "#54546d" }
|
||||||
|
|
||||||
|
[help]
|
||||||
|
on = { fg = "#7aa89f" }
|
||||||
|
run = { fg = "#957fb8" }
|
||||||
|
desc = {}
|
||||||
|
hovered = { reversed = true, bold = true }
|
||||||
|
footer = { fg = "#1f1f28", bg = "#dcd7ba" }
|
||||||
|
|
||||||
|
[notify]
|
||||||
|
title_info = { fg = "#98bb6c" }
|
||||||
|
title_warn = { fg = "#e6c384" }
|
||||||
|
title_error = { fg = "#ff5d62" }
|
||||||
|
|
||||||
|
[filetype]
|
||||||
|
rules = [
|
||||||
|
# images
|
||||||
|
{ mime = "image/*", fg = "#e6c384" },
|
||||||
|
|
||||||
|
# media
|
||||||
|
{ mime = "{audio,video}/*", fg = "#957fb8" },
|
||||||
|
|
||||||
|
# archives
|
||||||
|
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#e46876" },
|
||||||
|
|
||||||
|
# documents
|
||||||
|
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "#6a9589" },
|
||||||
|
|
||||||
|
# broken links
|
||||||
|
{ url = "*", is = "orphan", fg = "#c34043" },
|
||||||
|
|
||||||
|
# executables
|
||||||
|
{ url = "*", is = "exec", fg = "#76946a" },
|
||||||
|
|
||||||
|
# fallback
|
||||||
|
{ url = "*", fg = "#dcd7ba" },
|
||||||
|
{ url = "*/", fg = "#7e9cd8" },
|
||||||
|
]
|
||||||
BIN
yazi/.config/yazi/flavors/kanagawa.yazi/preview.png
Normal file
BIN
yazi/.config/yazi/flavors/kanagawa.yazi/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
448
yazi/.config/yazi/flavors/kanagawa.yazi/tmtheme.xml
Normal file
448
yazi/.config/yazi/flavors/kanagawa.yazi/tmtheme.xml
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Kanagawa</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#1F1F28</string>
|
||||||
|
<key>caret</key>
|
||||||
|
<string>#C8C093</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#DCD7BA</string>
|
||||||
|
<key>invisibles</key>
|
||||||
|
<string>#54546D</string>
|
||||||
|
<key>lineHighlight</key>
|
||||||
|
<string>#2D4F67</string>
|
||||||
|
<key>selection</key>
|
||||||
|
<string>#2D4F67</string>
|
||||||
|
<key>findHighlight</key>
|
||||||
|
<string>#2D4F67</string>
|
||||||
|
<key>selectionBorder</key>
|
||||||
|
<string>#222218</string>
|
||||||
|
<key>gutterForeground</key>
|
||||||
|
<string>#54546D</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Comment</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>comment</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#727169</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#98BB6C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Number</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.numeric</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#D27E99</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Built-in constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.language</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FFA066</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>User-defined constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character, constant.other</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6C384</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>#E6C384</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Ruby's @variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.other.readwrite.instance</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6C384</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>String interpolation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character.escaped, constant.character.escape, string source, string source.ruby</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#C0A36E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Keyword</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>keyword</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E46876</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#957FB8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#957FB8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Class name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7AA89F</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Inherited class</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.inherited-class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7AA89F</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7E9CD8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function argument</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.parameter</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#b8b4d0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.tag</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7FB4CA</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag attribute</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.attribute-name</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6C384</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library function</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7FB4CA</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.constant</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7FB4CA</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library class/type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.type, support.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7AA89F</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.other.variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FFA066</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string/>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FF5D62</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid deprecated</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid.deprecated</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#717C7C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#957FB8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.header</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.diff, meta.diff.header</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#7E9CD8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.deleted</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.deleted</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#43242B</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.inserted</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.inserted</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#2B3328</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>diff.changed</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>markup.changed</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#49443C</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.numeric.line-number.find-in-files - match</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#54546D</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.filename</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#C8C093</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>message.error</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E82424</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON Punctuation</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#9CABCA</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON Structure</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#957FB8</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>JSON String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>meta.structure.dictionary.value.json string.quoted.double.json</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#ffffff</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Escape Characters</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character.escape</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FF5D62</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Regular Expressions</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>string.regexp</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6C384</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>592FC036-6BB7-4676-A2F5-2894D48C8E33</string>
|
||||||
|
<key>colorSpaceName</key>
|
||||||
|
<string>sRGB</string>
|
||||||
|
<key>semanticClass</key>
|
||||||
|
<string>theme.dark.kanagawa</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
0
yazi/.config/yazi/flavors/monokai.yazi/LICENSE
Normal file
0
yazi/.config/yazi/flavors/monokai.yazi/LICENSE
Normal file
37
yazi/.config/yazi/flavors/monokai.yazi/README.md
Normal file
37
yazi/.config/yazi/flavors/monokai.yazi/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
Monokai flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
## 👀 Preview
|
||||||
|
|
||||||
|
<img src="preview.png" width="600" />
|
||||||
|
|
||||||
|
## 🎨 Installation
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ya pkg add malick-tammal/monokai
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Usage
|
||||||
|
|
||||||
|
Add it to your `theme.toml` as dark flavor:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
dark = "monokai"
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure your `theme.toml` doesn't contain anything other than `[flavor]`, unless you want to override certain styles of this flavor.
|
||||||
|
|
||||||
|
See the [Yazi flavor documentation](https://yazi-rs.github.io/docs/flavors/overview) for more details.
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
|
||||||
|
|
||||||
|
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.
|
||||||
182
yazi/.config/yazi/flavors/monokai.yazi/flavor.toml
Normal file
182
yazi/.config/yazi/flavors/monokai.yazi/flavor.toml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#----------------------------------------------------------
|
||||||
|
#-- HACK: Monokai
|
||||||
|
#----------------------------------------------------------
|
||||||
|
|
||||||
|
[mgr]
|
||||||
|
cwd = { fg = "#78DCE8" }
|
||||||
|
|
||||||
|
# Hovered
|
||||||
|
hovered = { reversed = true }
|
||||||
|
preview_hovered = { underline = true }
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find_symbol = " Finding: "
|
||||||
|
find_keyword = { fg = "#FF6188", bold = true, italic = true, underline = true }
|
||||||
|
find_position = { fg = "#AB9DF2", bg = "reset", bold = true, italic = true }
|
||||||
|
|
||||||
|
# Symlink
|
||||||
|
symlink_target = { fg = "#78DCE8", italic = true }
|
||||||
|
|
||||||
|
# Marker
|
||||||
|
marker_copied = { fg = "#A9DC76", bg = "#A9DC76" }
|
||||||
|
marker_cut = { fg = "lightred", bg = "lightred" }
|
||||||
|
marker_marked = { fg = "#AB9DF2", bg = "#AB9DF2" }
|
||||||
|
marker_selected = { fg = "#FF6188", bg = "#FF6188" }
|
||||||
|
|
||||||
|
# Count
|
||||||
|
count_copied = { fg = "#221F22", bg = "#A9DC76" }
|
||||||
|
count_cut = { fg = "#221F22", bg = "#FF6188" }
|
||||||
|
count_selected = { fg = "#221F22", bg = "#FFD866" }
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border_symbol = "│"
|
||||||
|
border_style = { fg = "#727072" }
|
||||||
|
|
||||||
|
# Tabs
|
||||||
|
[tabs]
|
||||||
|
active = { fg = "#221F22", bg = "#FFD866", bold = true }
|
||||||
|
inactive = { fg = "#FFD866", bg = "#221F22" }
|
||||||
|
|
||||||
|
# Separator
|
||||||
|
sep_inner = { open = "", close = "" }
|
||||||
|
sep_outer = { open = "", close = "" }
|
||||||
|
|
||||||
|
# Modes
|
||||||
|
[mode]
|
||||||
|
# Normal mode
|
||||||
|
normal_main = { fg = "#2D2A2E", bg = "#FFD866", bold = true }
|
||||||
|
normal_alt = { fg = "#FFD866", bg = "#403E41" }
|
||||||
|
|
||||||
|
# Select mode
|
||||||
|
select_main = { fg = "#2D2A2E", bg = "#AB9DF2", bold = true }
|
||||||
|
select_alt = { fg = "#AB9DF2", bg = "#403E41" }
|
||||||
|
|
||||||
|
# Unset mode
|
||||||
|
unset_main = { bg = "#FF6188", bold = true }
|
||||||
|
unset_alt = { fg = "#FF6188", bg = "gray" }
|
||||||
|
|
||||||
|
# Status bar
|
||||||
|
[status]
|
||||||
|
overall = {}
|
||||||
|
sep_left = { open = "", close = "" }
|
||||||
|
sep_right = { open = "", close = "" }
|
||||||
|
|
||||||
|
# Permissions
|
||||||
|
perm_sep = { fg = "#727072" }
|
||||||
|
perm_type = { fg = "#A9DC76" }
|
||||||
|
perm_read = { fg = "#FFD866" }
|
||||||
|
perm_write = { fg = "#FF6188" }
|
||||||
|
perm_exec = { fg = "#78DCE8" }
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
progress_label = { bold = true }
|
||||||
|
progress_normal = { fg = "#78DCE8", bg = "#2D2A2E" }
|
||||||
|
progress_error = { fg = "#FF6188", bg = "#2D2A2E" }
|
||||||
|
|
||||||
|
# Which
|
||||||
|
[which]
|
||||||
|
cols = 3
|
||||||
|
mask = { bg = "#2D2A2E" }
|
||||||
|
cand = { fg = "lightcyan" }
|
||||||
|
rest = { fg = "#727072" }
|
||||||
|
desc = { fg = "lightmagenta" }
|
||||||
|
separator = " "
|
||||||
|
separator_style = { fg = "#727072" }
|
||||||
|
|
||||||
|
# Confirmation
|
||||||
|
[confirm]
|
||||||
|
border = { fg = "#FFD866" }
|
||||||
|
title = { fg = "#FFD866" }
|
||||||
|
content = {}
|
||||||
|
list = {}
|
||||||
|
btn_yes = { reversed = true }
|
||||||
|
btn_no = {}
|
||||||
|
btn_labels = [" [Y]es ", " (N)o "]
|
||||||
|
|
||||||
|
# Spotter
|
||||||
|
[spot]
|
||||||
|
border = { fg = "#78DCE8" }
|
||||||
|
title = { fg = "#78DCE8" }
|
||||||
|
|
||||||
|
# Table
|
||||||
|
tbl_col = { fg = "#78DCE8" }
|
||||||
|
tbl_cell = { fg = "#FFD866", reversed = true }
|
||||||
|
|
||||||
|
# Norifications
|
||||||
|
[notify]
|
||||||
|
title_info = { fg = "#A9DC76" }
|
||||||
|
title_warn = { fg = "#FFD866" }
|
||||||
|
title_error = { fg = "#FF6188" }
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
icon_info = ""
|
||||||
|
icon_warn = ""
|
||||||
|
icon_error = ""
|
||||||
|
|
||||||
|
# Picker
|
||||||
|
[pick]
|
||||||
|
border = { fg = "#78DCE8" }
|
||||||
|
active = { fg = "#AB9DF2", bold = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# Input
|
||||||
|
[input]
|
||||||
|
border = { fg = "#78DCE8" }
|
||||||
|
title = {}
|
||||||
|
value = {}
|
||||||
|
selected = { reversed = true }
|
||||||
|
|
||||||
|
# Completion
|
||||||
|
[cmp]
|
||||||
|
border = { fg = "#78DCE8" }
|
||||||
|
active = { reversed = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
icon_file = ""
|
||||||
|
icon_folder = ""
|
||||||
|
icon_command = ""
|
||||||
|
|
||||||
|
# : Task manager {{{
|
||||||
|
[tasks]
|
||||||
|
border = { fg = "#78DCE8" }
|
||||||
|
title = {}
|
||||||
|
hovered = { fg = "#AB9DF2", underline = true }
|
||||||
|
|
||||||
|
# Help menu
|
||||||
|
[help]
|
||||||
|
on = { fg = "#78DCE8" }
|
||||||
|
run = { fg = "#AB9DF2" }
|
||||||
|
desc = {}
|
||||||
|
hovered = { reversed = true, bold = true }
|
||||||
|
footer = { fg = "#221F22", bg = "#FDFFF1" }
|
||||||
|
|
||||||
|
# File-specific styles
|
||||||
|
[filetype]
|
||||||
|
rules = [
|
||||||
|
# Images
|
||||||
|
{ mime = "image/*", fg = "#FFD866" },
|
||||||
|
|
||||||
|
# Media
|
||||||
|
{ mime = "{audio,video}/*", fg = "#A9DC76" },
|
||||||
|
|
||||||
|
# Archives
|
||||||
|
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#FF6188" },
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
{ mime = "application/{pdf,doc,rtf}", fg = "#78DCE8" },
|
||||||
|
|
||||||
|
# Empty files
|
||||||
|
{ mime = "inode/empty", fg = "#FF6188" },
|
||||||
|
|
||||||
|
# Special files
|
||||||
|
{ url = "*", is = "orphan", bg = "#FF6188" },
|
||||||
|
{ url = "*", is = "exec", fg = "#A9DC76" },
|
||||||
|
|
||||||
|
# Dummy files
|
||||||
|
{ url = "*", is = "dummy", bg = "#FF6188" },
|
||||||
|
{ url = "*/", is = "dummy", bg = "#FF6188" },
|
||||||
|
|
||||||
|
# Fallback
|
||||||
|
{ url = "*/", fg = "#FC9767", bold = true },
|
||||||
|
]
|
||||||
BIN
yazi/.config/yazi/flavors/monokai.yazi/preview.png
Normal file
BIN
yazi/.config/yazi/flavors/monokai.yazi/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 797 KiB |
300
yazi/.config/yazi/flavors/monokai.yazi/tmtheme.xml
Normal file
300
yazi/.config/yazi/flavors/monokai.yazi/tmtheme.xml
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>gutterSettings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#49483E</string>
|
||||||
|
<key>divider</key>
|
||||||
|
<string>#75715E</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#75715E</string>
|
||||||
|
</dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Monokai</string>
|
||||||
|
<key>semanticClass</key>
|
||||||
|
<string>theme.dark.monokai</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#272822</string>
|
||||||
|
<key>caret</key>
|
||||||
|
<string>#F8F8F0</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F2</string>
|
||||||
|
<key>invisibles</key>
|
||||||
|
<string>#49483E</string>
|
||||||
|
<key>lineHighlight</key>
|
||||||
|
<string>#49483E</string>
|
||||||
|
<key>selection</key>
|
||||||
|
<string>#49483E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Comment</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>comment</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#75715E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>String</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>string</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#E6DB74</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Number</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.numeric</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#AE81FF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Built-in constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.language</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#AE81FF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>User-defined constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>constant.character, constant.other</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#AE81FF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Keyword</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>keyword</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F92672</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F92672</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Storage type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>storage.type</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66D9EF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Class name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#A6E22E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Inherited class</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.inherited-class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic underline</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#A6E22E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#A6E22E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Function argument</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>variable.parameter</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#FD971F</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag name</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.name.tag</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F92672</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Tag attribute</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>entity.other.attribute-name</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#A6E22E</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library function</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.function</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66D9EF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library constant</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.constant</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66D9EF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library class/type</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.type, support.class</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string>italic</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#66D9EF</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Library variable</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>support.other.variable</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#F92672</string>
|
||||||
|
<key>fontStyle</key>
|
||||||
|
<string></string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Invalid deprecated</string>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>invalid.deprecated</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>background</key>
|
||||||
|
<string>#AE81FF</string>
|
||||||
|
<key>foreground</key>
|
||||||
|
<string>#F8F8F0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>D8D5E82E-3D5B-46B5-B38E-8C841C21347D</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
21
yazi/.config/yazi/flavors/tokyo-night.yazi/LICENSE
Normal file
21
yazi/.config/yazi/flavors/tokyo-night.yazi/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 - sxyazi
|
||||||
|
|
||||||
|
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.
|
||||||
21
yazi/.config/yazi/flavors/tokyo-night.yazi/LICENSE-tmtheme
Normal file
21
yazi/.config/yazi/flavors/tokyo-night.yazi/LICENSE-tmtheme
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Himanshu
|
||||||
|
|
||||||
|
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.
|
||||||
47
yazi/.config/yazi/flavors/tokyo-night.yazi/README.md
Normal file
47
yazi/.config/yazi/flavors/tokyo-night.yazi/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
Tokyo Night Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
## 👀 Preview
|
||||||
|
|
||||||
|
<img src="preview.png" width="600" />
|
||||||
|
|
||||||
|
## 🎨 Installation
|
||||||
|
|
||||||
|
### Using package manager
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ya pkg add BennyOe/tokyo-night
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/macOS
|
||||||
|
git clone https://github.com/BennyOe/tokyo-night.yazi.git ~/.config/yazi/flavors/tokyo-night.yazi
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
git clone https://github.com/BennyOe/tokyo-night.yazi.git %AppData%\yazi\config\flavors\tokyo-night.yazi
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Usage
|
||||||
|
|
||||||
|
Add the these lines to your `theme.toml` configuration file to use it:
|
||||||
|
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[flavor]
|
||||||
|
use = "tokyo-night"
|
||||||
|
# For Yazi 0.4 and above
|
||||||
|
dark = "tokyo-night"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
|
||||||
|
|
||||||
|
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.
|
||||||
201
yazi/.config/yazi/flavors/tokyo-night.yazi/flavor.toml
Normal file
201
yazi/.config/yazi/flavors/tokyo-night.yazi/flavor.toml
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
# : Manager {{{
|
||||||
|
|
||||||
|
[mgr]
|
||||||
|
cwd = { fg = "#7aa2f7" }
|
||||||
|
|
||||||
|
# Find
|
||||||
|
find_keyword = { fg = "#f7768e", bold = true, italic = true, underline = true }
|
||||||
|
find_position = { fg = "#bb9af7", bg = "reset", bold = true, italic = true }
|
||||||
|
|
||||||
|
# Marker
|
||||||
|
marker_copied = { fg = "#9ece6a", bg = "#9ece6a" }
|
||||||
|
marker_cut = { fg = "#e0af68", bg = "#f7768e" }
|
||||||
|
marker_marked = { fg = "#7aa2f7", bg = "#7dcfff" }
|
||||||
|
marker_selected = { fg = "#e0af68", bg = "#e0af68" }
|
||||||
|
|
||||||
|
# Count
|
||||||
|
count_copied = { fg = "#1a1b26", bg = "#9ece6a" }
|
||||||
|
count_cut = { fg = "#1a1b26", bg = "#e0af68" }
|
||||||
|
count_selected = { fg = "#1a1b26", bg = "#7aa2f7" }
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border_symbol = "│"
|
||||||
|
border_style = { fg = "#414868" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Tabs {{{
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
active = { fg = "#292e42", bg = "#7aa2f7", bold = true }
|
||||||
|
inactive = { fg = "#7aa2f7", bg = "#292e42" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Mode {{{
|
||||||
|
|
||||||
|
[mode]
|
||||||
|
|
||||||
|
normal_main = { fg = "#292e42", bg = "#7aa2f7", bold = true }
|
||||||
|
normal_alt = { fg = "#7aa2f7", bg = "#292e42" }
|
||||||
|
|
||||||
|
# Select mode
|
||||||
|
select_main = { fg = "#292e42", bg = "#9ece6a", bold = true }
|
||||||
|
select_alt = { fg = "#7aa2f7", bg = "#292e42" }
|
||||||
|
|
||||||
|
# Unset mode
|
||||||
|
unset_main = { fg = "#292e42", bg = "#bb9af7", bold = true }
|
||||||
|
unset_alt = { fg = "#7aa2f7", bg = "#292e42" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Status bar {{{
|
||||||
|
|
||||||
|
[status]
|
||||||
|
overall = { fg = "#7aa2f7" }
|
||||||
|
sep_left = { open = "", close = "" }
|
||||||
|
sep_right = { open = "", close = "" }
|
||||||
|
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
progress_label = { fg = "#292e42", bold = true }
|
||||||
|
progress_normal = { fg = "#7aa2f7", bg = "#292e42" }
|
||||||
|
progress_error = { fg = "#f7768e", bg = "#292e42" }
|
||||||
|
|
||||||
|
# Permissions
|
||||||
|
perm_sep = { fg = "#7aa2f7" }
|
||||||
|
perm_type = { fg = "#9ece6a" }
|
||||||
|
perm_read = { fg = "#e0af68" }
|
||||||
|
perm_write = { fg = "#f7768e" }
|
||||||
|
perm_exec = { fg = "#bb9af7" }
|
||||||
|
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
# : Pick {{{
|
||||||
|
|
||||||
|
[pick]
|
||||||
|
border = { fg = "#7aa2f7" }
|
||||||
|
active = { fg = "#bb9af7", bold = true }
|
||||||
|
inactive = {}
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Input {{{
|
||||||
|
|
||||||
|
[input]
|
||||||
|
border = { fg = "#7aa2f7" }
|
||||||
|
title = {}
|
||||||
|
value = {}
|
||||||
|
selected = { reversed = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Completion {{{
|
||||||
|
|
||||||
|
[cmp]
|
||||||
|
border = { fg = "#7aa2f7" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Tasks {{{
|
||||||
|
|
||||||
|
[tasks]
|
||||||
|
border = { fg = "#7aa2f7" }
|
||||||
|
title = {}
|
||||||
|
hovered = { fg = "#bb9af7", underline = true }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Which {{{
|
||||||
|
|
||||||
|
[which]
|
||||||
|
mask = { bg = "#414868" }
|
||||||
|
cand = { fg = "#9ece6a" }
|
||||||
|
rest = { fg = "#a9b1d6" }
|
||||||
|
desc = { fg = "#bb9af7" }
|
||||||
|
separator = " "
|
||||||
|
separator_style = { fg = "#626880" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Help {{{
|
||||||
|
|
||||||
|
[help]
|
||||||
|
on = { fg = "#9ece6a" }
|
||||||
|
run = { fg = "#bb9af7" }
|
||||||
|
hovered = { reversed = true, bold = true }
|
||||||
|
footer = { fg = "#1a1b26", bg = "#a9b1d6" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Spotter {{{
|
||||||
|
|
||||||
|
[spot]
|
||||||
|
border = { fg = "#7aa2f7" }
|
||||||
|
title = { fg = "#7aa2f7" }
|
||||||
|
tbl_col = { fg = "#9ece6a" }
|
||||||
|
tbl_cell = { fg = "#bb9af7", bg = "#292e42" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : Notify {{{
|
||||||
|
|
||||||
|
[notify]
|
||||||
|
title_info = { fg = "#9ece6a" }
|
||||||
|
title_warn = { fg = "#f7768e" }
|
||||||
|
title_error = { fg = "#e0af68" }
|
||||||
|
|
||||||
|
# : }}}
|
||||||
|
|
||||||
|
|
||||||
|
# : File-specific styles {{{
|
||||||
|
|
||||||
|
[filetype]
|
||||||
|
|
||||||
|
rules = [
|
||||||
|
# Images
|
||||||
|
{ mime = "image/*", fg = "#e0af68" },
|
||||||
|
|
||||||
|
# Media
|
||||||
|
{ mime = "video/*", fg = "#f7768e" },
|
||||||
|
{ mime = "audio/*", fg = "#f7768e" },
|
||||||
|
|
||||||
|
# Archives
|
||||||
|
{ mime = "application/zip", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-tar", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-bzip*", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-bzip2", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-7z-compressed", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-rar", fg = "#bb9af7" },
|
||||||
|
{ mime = "application/x-xz", fg = "#bb9af7" },
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
{ mime = "application/doc", fg = "#9ece6a" },
|
||||||
|
{ mime = "application/epub+zip", fg = "#9ece6a" },
|
||||||
|
{ mime = "application/pdf", fg = "#9ece6a" },
|
||||||
|
{ mime = "application/rtf", fg = "#9ece6a" },
|
||||||
|
{ mime = "application/vnd.*", fg = "#9ece6a" },
|
||||||
|
|
||||||
|
# Special files
|
||||||
|
{ mime = "*", is = "orphan", fg = "#f29cb4", bg = "#93000a" },
|
||||||
|
{ mime = "application/*exec*", fg = "#f7768e" },
|
||||||
|
|
||||||
|
# Fallback
|
||||||
|
{ url = "*", fg = "#c6d0f5" },
|
||||||
|
{ url = "*/", fg = "#7aa2f7" },
|
||||||
|
]
|
||||||
|
|
||||||
|
# : }}}
|
||||||
BIN
yazi/.config/yazi/flavors/tokyo-night.yazi/preview.png
Normal file
BIN
yazi/.config/yazi/flavors/tokyo-night.yazi/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 712 KiB |
1329
yazi/.config/yazi/flavors/tokyo-night.yazi/tmtheme.xml
Normal file
1329
yazi/.config/yazi/flavors/tokyo-night.yazi/tmtheme.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,93 +1,98 @@
|
|||||||
-- Change this to switch themes
|
-- Change this to switch themes
|
||||||
local THEME = "rose_pine"
|
-- local THEME = "kanagawa"
|
||||||
|
-- local THEME = "catppuccin-mocha"
|
||||||
|
-- local THEME = "everforest-medium"
|
||||||
|
local THEME = "gruvbox-material"
|
||||||
|
-- local THEME = "tokyo-night"
|
||||||
|
-- local THEME = "rose_pine-moon"
|
||||||
|
|
||||||
local theme_colors = {
|
-- local theme_colors = {
|
||||||
rose_pine = {
|
-- rose_pine = {
|
||||||
primary = "#c4a7e7", -- iris/mauve (purple-like color from rose-pine palette)
|
-- primary = "#c4a7e7", -- iris/mauve (purple-like color from rose-pine palette)
|
||||||
secondary = "#6e6a86", -- muted/subtle (comparable to comment/gray colors)
|
-- secondary = "#6e6a86", -- muted/subtle (comparable to comment/gray colors)
|
||||||
yanked = "#9ccfd8", -- foam (cyan-like accent color)
|
-- yanked = "#9ccfd8", -- foam (cyan-like accent color)
|
||||||
cut = "#eb6f92", -- love (red-like accent color)
|
-- cut = "#eb6f92", -- love (red-like accent color)
|
||||||
},
|
-- },
|
||||||
carbonfox = {
|
-- carbonfox = {
|
||||||
primary = "#78a9ff",
|
-- primary = "#78a9ff",
|
||||||
secondary = "#525252",
|
-- secondary = "#525252",
|
||||||
yanked = "#3ddbd9",
|
-- yanked = "#3ddbd9",
|
||||||
cut = "#ff7eb6",
|
-- cut = "#ff7eb6",
|
||||||
},
|
-- },
|
||||||
kanagawa = {
|
-- kanagawa = {
|
||||||
primary = "#7e9cd8",
|
-- primary = "#7e9cd8",
|
||||||
secondary = "#54546d",
|
-- secondary = "#54546d",
|
||||||
yanked = "#7aa89f",
|
-- yanked = "#7aa89f",
|
||||||
cut = "#e82424",
|
-- cut = "#e82424",
|
||||||
},
|
-- },
|
||||||
sonokai = {
|
-- sonokai = {
|
||||||
primary = "#76cce0",
|
-- primary = "#76cce0",
|
||||||
secondary = "#7f8490",
|
-- secondary = "#7f8490",
|
||||||
yanked = "#fc5d7c",
|
-- yanked = "#fc5d7c",
|
||||||
cut = "#f85e84",
|
-- cut = "#f85e84",
|
||||||
},
|
-- },
|
||||||
catppuccin = {
|
-- catppuccin = {
|
||||||
primary = "#89b4fa",
|
-- primary = "#89b4fa",
|
||||||
secondary = "#6c7086",
|
-- secondary = "#6c7086",
|
||||||
yanked = "#94e2d5",
|
-- yanked = "#94e2d5",
|
||||||
cut = "#f38ba8",
|
-- cut = "#f38ba8",
|
||||||
},
|
-- },
|
||||||
dracula = {
|
-- dracula = {
|
||||||
primary = "#bd93f9", -- purple
|
-- primary = "#bd93f9", -- purple
|
||||||
secondary = "#6272a4", -- comment
|
-- secondary = "#6272a4", -- comment
|
||||||
yanked = "#8be9fd", -- cyan
|
-- yanked = "#8be9fd", -- cyan
|
||||||
cut = "#ff5555", -- red
|
-- cut = "#ff5555", -- red
|
||||||
},
|
-- },
|
||||||
everforest = {
|
-- everforest = {
|
||||||
primary = "#7fbbb3", -- aqua
|
-- primary = "#7fbbb3", -- aqua
|
||||||
secondary = "#859289", -- gray
|
-- secondary = "#859289", -- gray
|
||||||
yanked = "#a7c080", -- green
|
-- yanked = "#a7c080", -- green
|
||||||
cut = "#e67e80", -- red
|
-- cut = "#e67e80", -- red
|
||||||
},
|
-- },
|
||||||
gruvbox = {
|
-- gruvbox = {
|
||||||
primary = "#7daea3", -- aqua
|
-- primary = "#7daea3", -- aqua
|
||||||
secondary = "#7c6f64", -- gray
|
-- secondary = "#7c6f64", -- gray
|
||||||
yanked = "#a9b665", -- green
|
-- yanked = "#a9b665", -- green
|
||||||
cut = "#ea6962", -- red
|
-- cut = "#ea6962", -- red
|
||||||
},
|
-- },
|
||||||
nightfox = {
|
-- nightfox = {
|
||||||
primary = "#719cd6", -- blue
|
-- primary = "#719cd6", -- blue
|
||||||
secondary = "#738091", -- comment
|
-- secondary = "#738091", -- comment
|
||||||
yanked = "#63cdcf", -- cyan
|
-- yanked = "#63cdcf", -- cyan
|
||||||
cut = "#c94f6d", -- red
|
-- cut = "#c94f6d", -- red
|
||||||
},
|
-- },
|
||||||
onedark = {
|
-- onedark = {
|
||||||
primary = "#61afef", -- blue
|
-- primary = "#61afef", -- blue
|
||||||
secondary = "#5c6370", -- gray
|
-- secondary = "#5c6370", -- gray
|
||||||
yanked = "#56b6c2", -- cyan
|
-- yanked = "#56b6c2", -- cyan
|
||||||
cut = "#e06c75", -- red
|
-- cut = "#e06c75", -- red
|
||||||
},
|
-- },
|
||||||
}
|
-- }
|
||||||
|
|
||||||
local c = theme_colors[THEME]
|
-- eocal c = theme_colors[THEME]
|
||||||
|
|
||||||
require("yaziline"):setup({
|
-- require("yaziline"):setup({
|
||||||
color = c.primary,
|
-- color = c.primary,
|
||||||
secondary_color = c.secondary,
|
-- secondary_color = c.secondary,
|
||||||
default_files_color = "darkgray",
|
-- default_files_color = "darkgray",
|
||||||
selected_files_color = "white",
|
-- selected_files_color = "white",
|
||||||
yanked_files_color = c.yanked,
|
-- yanked_files_color = c.yanked,
|
||||||
cut_files_color = c.cut,
|
-- cut_files_color = c.cut,
|
||||||
separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
|
-- separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
|
||||||
separator_open = "",
|
-- separator_open = "",
|
||||||
separator_close = "",
|
-- separator_close = "",
|
||||||
separator_open_thin = "",
|
-- separator_open_thin = "",
|
||||||
separator_close_thin = "",
|
-- separator_close_thin = "",
|
||||||
separator_head = "",
|
-- separator_head = "",
|
||||||
separator_tail = "",
|
-- separator_tail = "",
|
||||||
|
|
||||||
select_symbol = "",
|
-- select_symbol = "",
|
||||||
yank_symbol = "",
|
-- yank_symbol = "",
|
||||||
|
|
||||||
filename_max_length = 24, -- truncate when filename > 24
|
-- filename_max_length = 24, -- truncate when filename > 24
|
||||||
filename_truncate_length = 6, -- leave 6 chars on both sides
|
-- filename_truncate_length = 6, -- leave 6 chars on both sides
|
||||||
filename_truncate_separator = "...",
|
-- filename_truncate_separator = "...",
|
||||||
})
|
-- })
|
||||||
|
|
||||||
require("recycle-bin"):setup({
|
require("recycle-bin"):setup({
|
||||||
trash_dir = "~/.local/share/Trash/",
|
trash_dir = "~/.local/share/Trash/",
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ pwd = "osc7"
|
|||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[palette]
|
[palette]
|
||||||
gb_bright_path = "#9ccdf8"
|
gb_bright_path = "#7daea3"
|
||||||
gb_bright_git = "#f6c177"
|
gb_bright_git = "#d8a657"
|
||||||
gb_dark0_hard = "#3e7fb0"
|
gb_dark0_hard = "#3e7fb0"
|
||||||
gb_faded_green = "#79740e"
|
gb_faded_green = "#79740e"
|
||||||
gb_faded_red = "#ea9a97"
|
gb_faded_red = "#ea9a97"
|
||||||
gb_text = "#c4a7e7"
|
gb_text = "#d3869b"
|
||||||
|
|
||||||
[var]
|
[var]
|
||||||
user = ""
|
user = ""
|
||||||
@@ -21,14 +21,14 @@ alignment = "left"
|
|||||||
|
|
||||||
[[blocks.segments]]
|
[[blocks.segments]]
|
||||||
template = '{{ .HostName }} '
|
template = '{{ .HostName }} '
|
||||||
foreground = 'p:gb_text'
|
foreground = 'magenta'
|
||||||
background = 'transparent'
|
background = 'transparent'
|
||||||
type = 'text'
|
type = 'text'
|
||||||
style = 'plain'
|
style = 'plain'
|
||||||
|
|
||||||
[[blocks.segments]]
|
[[blocks.segments]]
|
||||||
template = '{{ .UserName }} '
|
template = '{{ .UserName }} '
|
||||||
foreground = 'p:gb_text'
|
foreground = 'magenta'
|
||||||
background = 'transparent'
|
background = 'transparent'
|
||||||
type = 'text'
|
type = 'text'
|
||||||
style = 'plain'
|
style = 'plain'
|
||||||
@@ -38,7 +38,7 @@ style = 'plain'
|
|||||||
|
|
||||||
[[blocks.segments]]
|
[[blocks.segments]]
|
||||||
template = "{{ .Path }} "
|
template = "{{ .Path }} "
|
||||||
foreground = "p:gb_bright_path"
|
foreground = "blue"
|
||||||
background = "transparent"
|
background = "transparent"
|
||||||
type = "path"
|
type = "path"
|
||||||
style = "plain"
|
style = "plain"
|
||||||
@@ -49,7 +49,7 @@ enable_hyperlink = true
|
|||||||
|
|
||||||
[[blocks.segments]]
|
[[blocks.segments]]
|
||||||
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
|
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
|
||||||
foreground = "p:gb_bright_git"
|
foreground = "yellow"
|
||||||
background = "transparent"
|
background = "transparent"
|
||||||
type = "git"
|
type = "git"
|
||||||
style = "plain"
|
style = "plain"
|
||||||
|
|||||||
103
yazi/.config/yazi/omp/omp_rose-pine.toml
Normal file
103
yazi/.config/yazi/omp/omp_rose-pine.toml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
"$schema" = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json"
|
||||||
|
pwd = "osc7"
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[palette]
|
||||||
|
gb_bright_path = "#9ccdf8"
|
||||||
|
gb_bright_git = "#f6c177"
|
||||||
|
gb_dark0_hard = "#3e7fb0"
|
||||||
|
gb_faded_green = "#79740e"
|
||||||
|
gb_faded_red = "#ea9a97"
|
||||||
|
gb_text = "#c4a7e7"
|
||||||
|
|
||||||
|
[var]
|
||||||
|
user = ""
|
||||||
|
ssh = "🌐"
|
||||||
|
|
||||||
|
# LEFT SIDE PROMPT
|
||||||
|
[[blocks]]
|
||||||
|
type = "prompt"
|
||||||
|
alignment = "left"
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = '{{ .HostName }} '
|
||||||
|
foreground = 'p:gb_text'
|
||||||
|
background = 'transparent'
|
||||||
|
type = 'text'
|
||||||
|
style = 'plain'
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = '{{ .UserName }} '
|
||||||
|
foreground = 'p:gb_text'
|
||||||
|
background = 'transparent'
|
||||||
|
type = 'text'
|
||||||
|
style = 'plain'
|
||||||
|
|
||||||
|
# [blocks.segments.properties]
|
||||||
|
# cache_duration = "none"
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = "{{ .Path }} "
|
||||||
|
foreground = "p:gb_bright_path"
|
||||||
|
background = "transparent"
|
||||||
|
type = "path"
|
||||||
|
style = "plain"
|
||||||
|
|
||||||
|
[blocks.segments.properties]
|
||||||
|
style = "full"
|
||||||
|
enable_hyperlink = true
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
|
||||||
|
foreground = "p:gb_bright_git"
|
||||||
|
background = "transparent"
|
||||||
|
type = "git"
|
||||||
|
style = "plain"
|
||||||
|
|
||||||
|
[blocks.segments.properties]
|
||||||
|
fetch_status = true
|
||||||
|
|
||||||
|
[blocks.segments.properties.status_formats]
|
||||||
|
Added = "+%d"
|
||||||
|
Deleted = "-%d"
|
||||||
|
Modified = "~%d"
|
||||||
|
Untracked = "?%d"
|
||||||
|
|
||||||
|
# RIGHT SIDE PROMPT
|
||||||
|
[[blocks]]
|
||||||
|
type = "rprompt"
|
||||||
|
alignment = "right"
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = " {{ .Full }}"
|
||||||
|
foreground = "p:gb_faded_green"
|
||||||
|
background = "transparent"
|
||||||
|
type = "node"
|
||||||
|
style = "plain"
|
||||||
|
|
||||||
|
[blocks.segments.properties]
|
||||||
|
display_mode = "files"
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = " {{ if .Venv }}({{ .Venv }}) {{ end }}{{ .Full }}"
|
||||||
|
foreground = "p:gb_faded_green"
|
||||||
|
background = "transparent"
|
||||||
|
type = "python"
|
||||||
|
style = "plain"
|
||||||
|
|
||||||
|
[blocks.segments.properties]
|
||||||
|
display_mode = "always"
|
||||||
|
fetch_virtual_env = true
|
||||||
|
|
||||||
|
# NEWLINE FOR INPUT
|
||||||
|
[[blocks]]
|
||||||
|
type = "prompt"
|
||||||
|
alignment = "left"
|
||||||
|
newline = true
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
template = "❯ "
|
||||||
|
foreground = "p:gb_bright_aqua"
|
||||||
|
background = "transparent"
|
||||||
|
type = "text"
|
||||||
|
style = "plain"
|
||||||
@@ -142,3 +142,33 @@ hash = "b951d27afe197d154f6da9683b46c5f8"
|
|||||||
use = "Mintass/rose-pine-moon"
|
use = "Mintass/rose-pine-moon"
|
||||||
rev = "94385fe"
|
rev = "94385fe"
|
||||||
hash = "edbe0dfb5db8ff37281dba62adc7e750"
|
hash = "edbe0dfb5db8ff37281dba62adc7e750"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "Chromium-3-Oxide/everforest-medium"
|
||||||
|
rev = "0158f0f"
|
||||||
|
hash = "a7c36abf0b13289aceb1c20a17c6a63e"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "bennyyip/gruvbox-dark"
|
||||||
|
rev = "619fdc5"
|
||||||
|
hash = "d4c7003ec6dc4efd5d3a050d5c2f2575"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "BennyOe/tokyo-night"
|
||||||
|
rev = "8e6296f"
|
||||||
|
hash = "d9da921c3bd37ba32ee27a8a7a40461f"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "dangooddd/kanagawa"
|
||||||
|
rev = "04985d1"
|
||||||
|
hash = "c69a63981e254a3a06a11c2bf0e6af1d"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "matt-dong-123/gruvbox-material"
|
||||||
|
rev = "6c36491"
|
||||||
|
hash = "564f2335540ee2c440c3b671bc28ac60"
|
||||||
|
|
||||||
|
[[flavor.deps]]
|
||||||
|
use = "malick-tammal/monokai"
|
||||||
|
rev = "e59f6cb"
|
||||||
|
hash = "dcb449d46e30dc020e4b7b59202d2448"
|
||||||
|
|||||||
@@ -843,6 +843,7 @@ local EXTS = {
|
|||||||
tfstate = "application/json",
|
tfstate = "application/json",
|
||||||
tfvars = "text/hcl",
|
tfvars = "text/hcl",
|
||||||
tga = "image/tga",
|
tga = "image/tga",
|
||||||
|
theme = "application/plain",
|
||||||
thmx = "application/ms-officetheme",
|
thmx = "application/ms-officetheme",
|
||||||
tif = "image/tiff",
|
tif = "image/tiff",
|
||||||
tiff = "image/tiff",
|
tiff = "image/tiff",
|
||||||
@@ -1049,15 +1050,13 @@ local EXTS = {
|
|||||||
zsh = "text/shellscript",
|
zsh = "text/shellscript",
|
||||||
}
|
}
|
||||||
|
|
||||||
local options = ya.sync(
|
local options = ya.sync(function(st)
|
||||||
function(st)
|
|
||||||
return {
|
return {
|
||||||
with_files = st.with_files,
|
with_files = st.with_files,
|
||||||
with_exts = st.with_exts,
|
with_exts = st.with_exts,
|
||||||
fallback_file1 = st.fallback_file1,
|
fallback_file1 = st.fallback_file1,
|
||||||
}
|
}
|
||||||
end
|
end)
|
||||||
)
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ tex /home/liph/Documents/tex D,t
|
|||||||
tank /mnt/tank t,t
|
tank /mnt/tank t,t
|
||||||
scripts /home/liph/scripts d,s
|
scripts /home/liph/scripts d,s
|
||||||
r /home/liph/Documents/r D,r
|
r /home/liph/Documents/r D,r
|
||||||
|
programming /home/liph/programming p
|
||||||
podman /mnt/flash1/podman t,p
|
podman /mnt/flash1/podman t,p
|
||||||
ohmyposh /home/liph/dotfiles/ohmyposh/.config/ohmyposh d,o
|
ohmyposh /home/liph/dotfiles/ohmyposh/.config/ohmyposh d,o
|
||||||
obsidian /home/liph/Documents/obsidian/vault D,o
|
obsidian /home/liph/Documents/obsidian/vault D,o
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
/home/liph/.config/yazi/themes/rose.toml
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
# vim:fileencoding=utf-8:foldmethod=marker
|
|
||||||
|
|
||||||
# : Manager {{{
|
|
||||||
|
|
||||||
[mgr]
|
|
||||||
cwd = { fg = "#94e2d5" }
|
|
||||||
|
|
||||||
# TODO: remove
|
|
||||||
# Hovered
|
|
||||||
hovered = { reversed = true }
|
|
||||||
preview_hovered = { underline = true }
|
|
||||||
|
|
||||||
# Find
|
|
||||||
find_keyword = { fg = "#f9e2af", bold = true, italic = true, underline = true }
|
|
||||||
find_position = { fg = "#f5c2e7", bg = "reset", bold = true, italic = true }
|
|
||||||
|
|
||||||
# Marker
|
|
||||||
marker_copied = { fg = "#a6e3a1", bg = "#a6e3a1" }
|
|
||||||
marker_cut = { fg = "#f38ba8", bg = "#f38ba8" }
|
|
||||||
marker_marked = { fg = "#94e2d5", bg = "#94e2d5" }
|
|
||||||
marker_selected = { fg = "#f9e2af", bg = "#f9e2af" }
|
|
||||||
|
|
||||||
# Count
|
|
||||||
count_copied = { fg = "#1e1e2e", bg = "#a6e3a1" }
|
|
||||||
count_cut = { fg = "#1e1e2e", bg = "#f38ba8" }
|
|
||||||
count_selected = { fg = "#1e1e2e", bg = "#f9e2af" }
|
|
||||||
|
|
||||||
# Border
|
|
||||||
border_symbol = "│"
|
|
||||||
border_style = { fg = "#7f849c" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Tabs {{{
|
|
||||||
|
|
||||||
[tabs]
|
|
||||||
active = { fg = "#1e1e2e", bg = "#89b4fa", bold = true }
|
|
||||||
inactive = { fg = "#89b4fa", bg = "#313244" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Mode {{{
|
|
||||||
|
|
||||||
[mode]
|
|
||||||
|
|
||||||
normal_main = { fg = "#1e1e2e", bg = "#89b4fa", bold = true }
|
|
||||||
normal_alt = { fg = "#89b4fa", bg = "#313244" }
|
|
||||||
|
|
||||||
# Select mode
|
|
||||||
select_main = { fg = "#1e1e2e", bg = "#94e2d5", bold = true }
|
|
||||||
select_alt = { fg = "#94e2d5", bg = "#313244" }
|
|
||||||
|
|
||||||
# Unset mode
|
|
||||||
unset_main = { fg = "#1e1e2e", bg = "#f2cdcd", bold = true }
|
|
||||||
unset_alt = { fg = "#f2cdcd", bg = "#313244" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Status bar {{{
|
|
||||||
|
|
||||||
[status]
|
|
||||||
# Permissions
|
|
||||||
perm_sep = { fg = "#7f849c" }
|
|
||||||
perm_type = { fg = "#89b4fa" }
|
|
||||||
perm_read = { fg = "#f9e2af" }
|
|
||||||
perm_write = { fg = "#f38ba8" }
|
|
||||||
perm_exec = { fg = "#a6e3a1" }
|
|
||||||
|
|
||||||
# Progress
|
|
||||||
progress_label = { fg = "#ffffff", bold = true }
|
|
||||||
progress_normal = { fg = "#a6e3a1", bg = "#45475a" }
|
|
||||||
progress_error = { fg = "#f9e2af", bg = "#f38ba8" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Pick {{{
|
|
||||||
|
|
||||||
[pick]
|
|
||||||
border = { fg = "#89b4fa" }
|
|
||||||
active = { fg = "#f5c2e7", bold = true }
|
|
||||||
inactive = {}
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Input {{{
|
|
||||||
|
|
||||||
[input]
|
|
||||||
border = { fg = "#89b4fa" }
|
|
||||||
title = {}
|
|
||||||
value = {}
|
|
||||||
selected = { reversed = true }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Completion {{{
|
|
||||||
|
|
||||||
[cmp]
|
|
||||||
border = { fg = "#89b4fa" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Tasks {{{
|
|
||||||
|
|
||||||
[tasks]
|
|
||||||
border = { fg = "#89b4fa" }
|
|
||||||
title = {}
|
|
||||||
hovered = { fg = "#f5c2e7", bold = true }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Which {{{
|
|
||||||
|
|
||||||
[which]
|
|
||||||
mask = { bg = "#313244" }
|
|
||||||
cand = { fg = "#94e2d5" }
|
|
||||||
rest = { fg = "#9399b2" }
|
|
||||||
desc = { fg = "#f5c2e7" }
|
|
||||||
separator = " "
|
|
||||||
separator_style = { fg = "#585b70" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Help {{{
|
|
||||||
|
|
||||||
[help]
|
|
||||||
on = { fg = "#94e2d5" }
|
|
||||||
run = { fg = "#f5c2e7" }
|
|
||||||
hovered = { reversed = true, bold = true }
|
|
||||||
footer = { fg = "#313244", bg = "#cdd6f4" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Spotter {{{
|
|
||||||
|
|
||||||
[spot]
|
|
||||||
border = { fg = "#89b4fa" }
|
|
||||||
title = { fg = "#89b4fa" }
|
|
||||||
tbl_col = { fg = "#94e2d5" }
|
|
||||||
tbl_cell = { fg = "#f5c2e7", bg = "#45475a" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : Notification {{{
|
|
||||||
|
|
||||||
[notify]
|
|
||||||
title_info = { fg = "#a6e3a1" }
|
|
||||||
title_warn = { fg = "#f9e2af" }
|
|
||||||
title_error = { fg = "#f38ba8" }
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
|
|
||||||
|
|
||||||
# : File-specific styles {{{
|
|
||||||
|
|
||||||
[filetype]
|
|
||||||
|
|
||||||
rules = [
|
|
||||||
# Image
|
|
||||||
{ mime = "image/*", fg = "#94e2d5" },
|
|
||||||
# Media
|
|
||||||
{ mime = "{audio,video}/*", fg = "#f9e2af" },
|
|
||||||
# Archive
|
|
||||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#f5c2e7" },
|
|
||||||
# Document
|
|
||||||
{ mime = "application/{pdf,doc,rtf}", fg = "#a6e3a1" },
|
|
||||||
# Virtual file system
|
|
||||||
{ mime = "vfs/{absent,stale}", fg = "#9399b2" },
|
|
||||||
# Fallback
|
|
||||||
{ url = "*", fg = "#cdd6f4" },
|
|
||||||
{ url = "*/", fg = "#89b4fa" },
|
|
||||||
# TODO: remove
|
|
||||||
{ name = "*", fg = "#cdd6f4" },
|
|
||||||
{ name = "*/", fg = "#89b4fa" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# : }}}
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
# Theme Configuration
|
|
||||||
[theme]
|
|
||||||
# Theme metadata
|
|
||||||
name = "Dracula Pro"
|
|
||||||
author = "Enhanced Dracula Theme"
|
|
||||||
version = "2.0.0"
|
|
||||||
description = "A sophisticated dark theme based on Dracula color scheme"
|
|
||||||
|
|
||||||
# Color palette definition
|
|
||||||
[theme.colors]
|
|
||||||
background = "#282a36"
|
|
||||||
current_line = "#44475a"
|
|
||||||
foreground = "#f8f8f2"
|
|
||||||
comment = "#6272a4"
|
|
||||||
purple = "#bd93f9"
|
|
||||||
green = "#50fa7b"
|
|
||||||
orange = "#ffb86c"
|
|
||||||
red = "#ff5555"
|
|
||||||
pink = "#ff79c6"
|
|
||||||
cyan = "#8be9fd"
|
|
||||||
yellow = "#f1fa8c"
|
|
||||||
|
|
||||||
# Animation settings
|
|
||||||
[theme.animation]
|
|
||||||
frames_per_second = 60
|
|
||||||
duration = 0.2
|
|
||||||
easing = "easeInOutCubic"
|
|
||||||
|
|
||||||
# Manager
|
|
||||||
[manager]
|
|
||||||
# Enhanced preview options
|
|
||||||
preview = { tab_size = 2, max_width = 100, max_height = 50, cache_size = 50, scroll_offset = 5 }
|
|
||||||
preview_ratios = [1, 4, 4]
|
|
||||||
preview_shown = true
|
|
||||||
preview_service = { image = "ueberzug", video = "ffmpegthumbnailer", pdf = "pdftoppm" }
|
|
||||||
|
|
||||||
# Sophisticated hover effects
|
|
||||||
hovered = { fg = "#f8f8f2", bg = "#44475a", italic = true }
|
|
||||||
|
|
||||||
# Enhanced markers with animations
|
|
||||||
marker_copied = { fg = "#282a36", bg = "#50fa7b" }
|
|
||||||
|
|
||||||
# Dynamic loading indicators
|
|
||||||
loading_indicator_frames = "⣾⣽⣻⢿⡿⣟⣯⣷"
|
|
||||||
loading_style = { fg = "#bd93f9", bold = true }
|
|
||||||
|
|
||||||
# Enhanced folder icons
|
|
||||||
folder_icons = { default = " ", open = " ", empty = " ", empty_open = " ", symlink = " ", symlink_open = " ", error = " " }
|
|
||||||
|
|
||||||
file_size_units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]
|
|
||||||
|
|
||||||
# Status
|
|
||||||
[status]
|
|
||||||
# Dynamic status bar
|
|
||||||
refresh_rate = 1000
|
|
||||||
separator_open = ""
|
|
||||||
separator_close = ""
|
|
||||||
|
|
||||||
# Progress bar styling
|
|
||||||
progress_bar_style = { fg = "#bd93f9", bg = "#44475a" }
|
|
||||||
|
|
||||||
# Enhanced modes
|
|
||||||
mode_normal = { fg = "#282a36", bg = "#bd93f9", bold = true }
|
|
||||||
|
|
||||||
# Input
|
|
||||||
[input]
|
|
||||||
# Advanced input styling
|
|
||||||
cursor_style = { fg = "#f8f8f2", bg = "#6272a4", blink = true, blink_interval = 500 }
|
|
||||||
|
|
||||||
# History features
|
|
||||||
history_size = 100
|
|
||||||
history_unique = true
|
|
||||||
|
|
||||||
# Completion styling
|
|
||||||
completion_style = { selected_bg = "#44475a", selected_fg = "#f8f8f2", selected_bold = true, selected_italic = true }
|
|
||||||
|
|
||||||
# Notify
|
|
||||||
[notify]
|
|
||||||
# Enhanced notification system
|
|
||||||
position = "top-right"
|
|
||||||
timeout = 5000
|
|
||||||
max_width = 400
|
|
||||||
max_height = 200
|
|
||||||
margin = 10
|
|
||||||
padding = 8
|
|
||||||
|
|
||||||
[notify.levels]
|
|
||||||
info = { fg = "#50fa7b", bg = "#282a36", icon = " ", timeout = 3000 }
|
|
||||||
warn = { fg = "#f1fa8c", bg = "#282a36", icon = " ", timeout = 5000 }
|
|
||||||
error = { fg = "#ff5555", bg = "#282a36", icon = " ", timeout = 7000 }
|
|
||||||
debug = { fg = "#6272a4", bg = "#282a36", icon = " ", timeout = 2000 }
|
|
||||||
|
|
||||||
[notify.animation]
|
|
||||||
enabled = true
|
|
||||||
duration = 200
|
|
||||||
slide_in = "right"
|
|
||||||
fade_out = true
|
|
||||||
|
|
||||||
[notify.border]
|
|
||||||
fg = "#bd93f9"
|
|
||||||
bg = "#282a36"
|
|
||||||
style = "rounded"
|
|
||||||
|
|
||||||
[notify.overlay]
|
|
||||||
bg = "#282a36"
|
|
||||||
blend = 0.8
|
|
||||||
|
|
||||||
# File-specific styles
|
|
||||||
[filetype]
|
|
||||||
rules = [
|
|
||||||
# Development Environment
|
|
||||||
{ name = ".env*", fg = "#50fa7b", bold = true, prefix = " " },
|
|
||||||
{ name = ".git*", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
{ name = ".docker*", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Configuration Files
|
|
||||||
{ name = "*.{json,yaml,yml,toml,xml}", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{ini,conf,cfg}", fg = "#6272a4", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Web Development
|
|
||||||
{ name = "*.{html,htm}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{css,scss,sass,less}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{jsx,tsx}", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{js,ts}", fg = "#f1fa8c", italic = true, prefix = " " },
|
|
||||||
{ name = "*.vue", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
{ name = "*.svelte", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Backend Development
|
|
||||||
{ name = "*.{py,pyc}", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{rb,erb}", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{php,phar}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{java,jar}", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
{ name = "*.go", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
{ name = "*.rs", fg = "#ff7043", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# System Programming
|
|
||||||
{ name = "*.{c,h}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{cpp,hpp}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{asm,s}", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Build Systems
|
|
||||||
{ name = "*Makefile", fg = "#ff79c6", bold = true, prefix = " " },
|
|
||||||
{ name = "*CMakeLists.txt", fg = "#ff79c6", bold = true, prefix = " " },
|
|
||||||
{ name = "*.gradle", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Package Managers
|
|
||||||
{ name = "package.json", fg = "#ff5555", bold = true, prefix = " " },
|
|
||||||
{ name = "package-lock.json", fg = "#ff5555", italic = true, prefix = " " },
|
|
||||||
{ name = "composer.json", fg = "#ff79c6", bold = true, prefix = " " },
|
|
||||||
{ name = "Cargo.toml", fg = "#ff7043", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
{ name = "*.{md,mdx}", fg = "#f1fa8c", italic = true, prefix = " " },
|
|
||||||
{ name = "*.rst", fg = "#f1fa8c", italic = true, prefix = " " },
|
|
||||||
{ name = "*.pdf", fg = "#ff5555", bold = true, prefix = " " },
|
|
||||||
{ name = "LICENSE*", fg = "#50fa7b", bold = true, prefix = " " },
|
|
||||||
{ name = "README*", fg = "#50fa7b", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Media with size categories
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 1MB", fg = "#8be9fd", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 10MB", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "> 10MB", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Video files with duration indicator
|
|
||||||
{ name = "*.{mp4,mkv}", duration = "< 10:00", fg = "#bd93f9", prefix = " " },
|
|
||||||
{ name = "*.{mp4,mkv}", duration = "< 1:00:00", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{mp4,mkv}", duration = "> 1:00:00", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Archives with compression ratio
|
|
||||||
{ name = "*.{zip,gz,tar}", ratio = "< 0.5", fg = "#50fa7b", prefix = " " },
|
|
||||||
{ name = "*.{zip,gz,tar}", ratio = "< 0.7", fg = "#f1fa8c", prefix = " " },
|
|
||||||
{ name = "*.{zip,gz,tar}", ratio = "> 0.7", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Special Directories
|
|
||||||
{ name = "node_modules", fg = "#6272a4", prefix = " " },
|
|
||||||
{ name = ".git", fg = "#ff5555", prefix = " " },
|
|
||||||
{ name = ".github", fg = "#bd93f9", prefix = " " },
|
|
||||||
{ name = "dist", fg = "#6272a4", prefix = " " },
|
|
||||||
{ name = "build", fg = "#6272a4", prefix = " " },
|
|
||||||
|
|
||||||
# Additional file types
|
|
||||||
# Audio files
|
|
||||||
{ name = "*.{mp3,flac,m4a,wav,ogg}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Font files
|
|
||||||
{ name = "*.{ttf,otf,woff,woff2}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Database files
|
|
||||||
{ name = "*.{sql,sqlite,db}", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Shell scripts
|
|
||||||
{ name = "*.{sh,bash,zsh,fish}", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Virtual environments
|
|
||||||
{ name = "venv", fg = "#6272a4", prefix = " " },
|
|
||||||
{ name = ".env", fg = "#50fa7b", prefix = " " },
|
|
||||||
|
|
||||||
# Container files
|
|
||||||
{ name = "*.dockerfile", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
{ name = "docker-compose*.{yml,yaml}", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Security files
|
|
||||||
{ name = "*.{pem,crt,ca,key}", fg = "#ff5555", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Improved size-based rules for media
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 100KB", fg = "#8be9fd", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 1MB", fg = "#bd93f9", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 10MB", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "> 10MB", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Default Fallbacks
|
|
||||||
{ name = "*", fg = "#f8f8f2" },
|
|
||||||
{ name = "*/", fg = "#bd93f9", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Additional Development Files
|
|
||||||
{ name = "*.{proto}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{graphql,gql}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{tf,tfvars}", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Container and Cloud
|
|
||||||
{ name = "*.{yaml,yml}", pattern = "^k8s|^kubernetes", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
{ name = "*.{yaml,yml}", pattern = "^helm", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# Data Files
|
|
||||||
{ name = "*.{csv,tsv}", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{parquet,avro}", fg = "#bd93f9", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# Size-based rules for documents
|
|
||||||
{ name = "*.{pdf,epub,mobi}", size = "< 1MB", fg = "#8be9fd", prefix = " " },
|
|
||||||
{ name = "*.{pdf,epub,mobi}", size = "< 10MB", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{pdf,epub,mobi}", size = "> 10MB", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Additional Development Files
|
|
||||||
{ name = "*.{sol}", fg = "#bd93f9", italic = true, prefix = " " }, # Solidity files
|
|
||||||
{ name = "*.{ex,exs}", fg = "#bd93f9", italic = true, prefix = " " }, # Elixir files
|
|
||||||
{ name = "*.{kt,kts}", fg = "#ff79c6", italic = true, prefix = " " }, # Kotlin files
|
|
||||||
{ name = "*.{swift}", fg = "#ff5555", italic = true, prefix = " " }, # Swift files
|
|
||||||
|
|
||||||
# Config Files
|
|
||||||
{ name = "*.{nginx,nginx.conf}", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
{ name = "*{webpack}*", fg = "#8be9fd", bold = true, prefix = " " },
|
|
||||||
|
|
||||||
# ML/Data Science
|
|
||||||
{ name = "*.{ipynb}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{pkl,pickle}", fg = "#50fa7b", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# 3D/Graphics
|
|
||||||
{ name = "*.{blend}", fg = "#ff79c6", italic = true, prefix = " " },
|
|
||||||
{ name = "*.{fbx,obj,stl}", fg = "#8be9fd", italic = true, prefix = " " },
|
|
||||||
|
|
||||||
# More granular size-based rules for media files
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 50KB", fg = "#8be9fd", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 500KB", fg = "#bd93f9", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 2MB", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "< 20MB", fg = "#ff7043", prefix = " " },
|
|
||||||
{ name = "*.{jpg,jpeg,png,gif}", size = "> 20MB", fg = "#ff5555", prefix = " " },
|
|
||||||
|
|
||||||
# Size categories for video files
|
|
||||||
{ name = "*.{mp4,mkv,avi,mov}", size = "< 100MB", fg = "#8be9fd", prefix = " " },
|
|
||||||
{ name = "*.{mp4,mkv,avi,mov}", size = "< 1GB", fg = "#ffb86c", prefix = " " },
|
|
||||||
{ name = "*.{mp4,mkv,avi,mov}", size = "> 1GB", fg = "#ff5555", prefix = " " },
|
|
||||||
]
|
|
||||||
|
|
||||||
# Keybindings
|
|
||||||
[keys]
|
|
||||||
# Visual key hints
|
|
||||||
show_hints = true
|
|
||||||
hint_style = { fg = "#6272a4", bg = "#44475a", italic = true }
|
|
||||||
|
|
||||||
# Command palette
|
|
||||||
command_palette = { bg = "#282a36", fg = "#f8f8f2", selected_bg = "#44475a", selected_fg = "#f8f8f2", border = "#bd93f9" }
|
|
||||||
|
|
||||||
# Preview
|
|
||||||
[preview]
|
|
||||||
tab_size = 2
|
|
||||||
max_width = 120
|
|
||||||
max_height = 60
|
|
||||||
cache_dir = "/tmp/yazi"
|
|
||||||
|
|
||||||
[preview.image]
|
|
||||||
enabled = true
|
|
||||||
format = "rgb"
|
|
||||||
max_width = 1920
|
|
||||||
max_height = 1080
|
|
||||||
quality = 90
|
|
||||||
animate = true
|
|
||||||
cache = true
|
|
||||||
|
|
||||||
[preview.preview_service]
|
|
||||||
image = "ueberzug"
|
|
||||||
video = "ffmpegthumbnailer"
|
|
||||||
pdf = "pdftoppm"
|
|
||||||
epub = "epub-thumbnailer"
|
|
||||||
office = "libreoffice --headless --convert-to pdf"
|
|
||||||
markdown = "glow"
|
|
||||||
|
|
||||||
[preview.syntax]
|
|
||||||
theme = "Dracula"
|
|
||||||
background = "#282a36"
|
|
||||||
|
|
||||||
# Opener
|
|
||||||
[opener]
|
|
||||||
edit = [
|
|
||||||
{ exec = 'nvim "$@"', desc = "Edit with Neovim" },
|
|
||||||
{ exec = 'code "$@"', desc = "Edit with VS Code" }
|
|
||||||
]
|
|
||||||
open = [
|
|
||||||
{ exec = 'xdg-open "$@"', desc = "Open with system default" },
|
|
||||||
{ exec = 'firefox "$@"', desc = "Open in Firefox" }
|
|
||||||
]
|
|
||||||
reveal = [
|
|
||||||
{ exec = 'nautilus "$@"', desc = "Reveal in file manager" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# Enhanced preview features
|
|
||||||
max_preview_size = 10485760 # 10MB limit for preview
|
|
||||||
scroll_offset = 5
|
|
||||||
scroll_smooth = true
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
# Gruvbox Material Dark theme for Yazi
|
|
||||||
# Based on https://github.com/sainnhe/gruvbox-material
|
|
||||||
|
|
||||||
[manager]
|
|
||||||
cwd = { fg = "#d8a657" } # yellow
|
|
||||||
|
|
||||||
# Hovered
|
|
||||||
hovered = { fg = "#282828", bg = "#a9b665" } # green background
|
|
||||||
preview_hovered = { underline = true }
|
|
||||||
|
|
||||||
# Find
|
|
||||||
find_keyword = { fg = "#282828", bg = "#d8a657" } # yellow
|
|
||||||
find_position = { fg = "#282828", bg = "#7daea3" } # aqua
|
|
||||||
|
|
||||||
# Marker
|
|
||||||
marker_copied = { fg = "#a9b665", bg = "#a9b665" } # green
|
|
||||||
marker_cut = { fg = "#ea6962", bg = "#ea6962" } # red
|
|
||||||
marker_marked = { fg = "#7daea3", bg = "#7daea3" } # aqua
|
|
||||||
marker_selected = { fg = "#d8a657", bg = "#d8a657" } # yellow
|
|
||||||
|
|
||||||
# Tab
|
|
||||||
tab_active = { fg = "#282828", bg = "#a9b665" } # green
|
|
||||||
tab_inactive = { fg = "#d4be98", bg = "#3c3836" } # fg on bg1
|
|
||||||
tab_width = 1
|
|
||||||
|
|
||||||
# Count
|
|
||||||
count_copied = { fg = "#282828", bg = "#a9b665" } # green
|
|
||||||
count_cut = { fg = "#282828", bg = "#ea6962" } # red
|
|
||||||
count_selected = { fg = "#282828", bg = "#7daea3" } # aqua
|
|
||||||
|
|
||||||
# Border
|
|
||||||
border_symbol = "│"
|
|
||||||
border_style = { fg = "#504945" } # bg2
|
|
||||||
|
|
||||||
# Syntect theme for preview
|
|
||||||
syntect_theme = "~/.config/yazi/Gruvbox-Material-Dark.tmTheme"
|
|
||||||
|
|
||||||
[status]
|
|
||||||
separator_open = ""
|
|
||||||
separator_close = ""
|
|
||||||
separator_style = { fg = "#504945", bg = "#504945" } # bg2
|
|
||||||
|
|
||||||
# Mode
|
|
||||||
mode_normal = { fg = "#282828", bg = "#7daea3", bold = true } # aqua
|
|
||||||
mode_select = { fg = "#282828", bg = "#d8a657", bold = true } # yellow
|
|
||||||
mode_unset = { fg = "#282828", bg = "#ea6962", bold = true } # red
|
|
||||||
|
|
||||||
# Progress
|
|
||||||
progress_label = { fg = "#d4be98", bold = true } # fg
|
|
||||||
progress_normal = { fg = "#7daea3", bg = "#3c3836" } # aqua on bg1
|
|
||||||
progress_error = { fg = "#ea6962", bg = "#3c3836" } # red on bg1
|
|
||||||
|
|
||||||
# Permissions
|
|
||||||
permissions_t = { fg = "#a9b665" } # green
|
|
||||||
permissions_r = { fg = "#d8a657" } # yellow
|
|
||||||
permissions_w = { fg = "#ea6962" } # red
|
|
||||||
permissions_x = { fg = "#7daea3" } # aqua
|
|
||||||
permissions_s = { fg = "#504945" } # bg2
|
|
||||||
|
|
||||||
[input]
|
|
||||||
border = { fg = "#7daea3" } # aqua
|
|
||||||
title = { fg = "#d4be98" } # fg
|
|
||||||
value = { fg = "#d4be98" } # fg
|
|
||||||
selected = { reversed = true }
|
|
||||||
|
|
||||||
[select]
|
|
||||||
border = { fg = "#7daea3" } # aqua
|
|
||||||
active = { fg = "#d8a657" } # yellow
|
|
||||||
inactive = { fg = "#7c6f64" } # bg3
|
|
||||||
|
|
||||||
[completion]
|
|
||||||
border = { fg = "#7daea3" } # aqua
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[flavor]
|
|
||||||
dark = "rose-pine-moon"
|
|
||||||
# or "rose-pine-moon"
|
|
||||||
# or light = "rose-pine-dawn"
|
|
||||||
Reference in New Issue
Block a user