Compare commits
37 Commits
ee2b1cb5ce
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 658498860e | |||
| 794f26df04 | |||
| 88e0293307 | |||
| ceca3f7b4e | |||
| a1c09a6d3b | |||
| f12b027aa5 | |||
| a22912ce30 | |||
| a5be19430e | |||
| d5d9075ad2 | |||
| 845ef5d076 | |||
| 07d8051731 | |||
| 1baf5ecddf | |||
| 65658a34b8 | |||
| f901141e5f | |||
| 8925d9677e | |||
| e6e444fff7 | |||
| 84b025eb0e | |||
| e32afefde9 | |||
| f5ac9bce3e | |||
| ab69cc08f2 | |||
| 7b590ef2ec | |||
| b2194f79dd | |||
| 5a78d47050 | |||
| 001c74dfa7 | |||
| 8b28075f40 | |||
| 2e1ccf0cc2 | |||
| f3d1e7fdb1 | |||
| 1b652298ef | |||
| 0ffe6606b6 | |||
| b9fe2697a0 | |||
| 5fc9e27e67 | |||
| c4df37dacb | |||
| d7b808ce59 | |||
| b7cfdb00a3 | |||
| ce7e3a9824 | |||
| 70c6316f8e | |||
| cdc1aa61c4 |
@@ -0,0 +1,44 @@
|
|||||||
|
# Agent Guidelines: Aerc Configuration Repository
|
||||||
|
|
||||||
|
This repository contains the configuration and customization for `aerc`, a terminal-based email client. It is optimized for a workflow involving `notmuch` for indexing, `mbsync` for synchronization, `abook` for address management, `khal` for calendar, and `taskwarrior` for tasks.
|
||||||
|
|
||||||
|
## 1. Build, Lint, and Test Commands
|
||||||
|
|
||||||
|
### Sync and Maintenance
|
||||||
|
- **Global Sync**: Run `mailsync` (bound to `u` in `aerc`) to perform:
|
||||||
|
1. `mbsync -a` (Mail sync)
|
||||||
|
2. `vdirsyncer sync` (Calendar sync)
|
||||||
|
3. `caldawarrior sync` (Task sync)
|
||||||
|
4. `notmuch new` for all account databases (Indexing)
|
||||||
|
5. `notmuch-abook-sync` (Address book population)
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
- **Permissions**: `accounts.conf` must be `0600`.
|
||||||
|
- **Dependencies**: `w3m`, `bat`, `yazi`, `notmuch`, `abook`, `pass`, `isync`, `khal`, `vdirsyncer`, `task`, `taskwarrior-tui`, `caldawarrior`.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- **Filters**: `cat email.html | w3m -T text/html -dump`
|
||||||
|
- **Calendar**: `khal list`
|
||||||
|
- **Tasks**: `task list`
|
||||||
|
|
||||||
|
## 2. Code Style Guidelines
|
||||||
|
|
||||||
|
### Aerc Configuration
|
||||||
|
- **Notmuch URI**: Use absolute triple-slash format: `source = notmuch:///home/liph/Mail/account_name`.
|
||||||
|
- **Credentials**: Use `pass` via `outgoing-cred-cmd = pass show mail/account_email`.
|
||||||
|
- **Sidebar**: Use separate `notmuch` databases per account directory for isolation.
|
||||||
|
|
||||||
|
### Keybindings
|
||||||
|
- **Leader**: `,` (comma).
|
||||||
|
- **Tab switching**: `Alt+n` (next) and `Alt+p` (prev).
|
||||||
|
- **Contacts**: `,ab` opens `abook`.
|
||||||
|
- **Calendar**: `,ca` opens unified `khal`.
|
||||||
|
- **Tasks**: `,ta` opens `taskwarrior-tui`.
|
||||||
|
|
||||||
|
### Terminal Tabs (Khal/Taskwarrior)
|
||||||
|
- **Escape**: Press `Ctrl+x` to release terminal focus and use `aerc` commands.
|
||||||
|
- **Tab Passthrough**: `Tab` and `Backtab` are passed to terminal apps for internal navigation.
|
||||||
|
|
||||||
|
### Neovim integration (`after/ftplugin/mail.lua`)
|
||||||
|
- **Completion**: Triggered by `Tab` on header lines using `abook --mutt-query`.
|
||||||
|
- **Fzf**: Triggered by `Ctrl+f` for interactive contact selection.
|
||||||
@@ -1,50 +1,33 @@
|
|||||||
[phil_live]
|
[phil]
|
||||||
source = imaps://phil%40liphlink.xyz:Fqi5UAyr46e69fG@mail.liphlink.xyz:993
|
source = notmuch:///home/liph/Mail/phil
|
||||||
outgoing = smtp://phil%40liphlink.xyz:Fqi5UAyr46e69fG@mail.liphlink.xyz:587
|
query-map = ~/.config/aerc/phil.queries
|
||||||
default = INBOX
|
|
||||||
from = Phil <phil@liphlink.xyz>
|
|
||||||
copy-to = Sent
|
|
||||||
archive = Archive
|
|
||||||
postpone = Drafts
|
|
||||||
folders-sort = INBOX,Draftr,Sent,Archive,Spam,Trash
|
|
||||||
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
|
|
||||||
|
|
||||||
[phil_notmuch]
|
|
||||||
source = notmuch://~/Mail/phil
|
|
||||||
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
|
||||||
|
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]
|
||||||
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
|
source = notmuch:///home/liph/Mail/spam
|
||||||
outgoing = smtp://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:587
|
|
||||||
default = INBOX
|
|
||||||
from = Liph <spam@liphlink.xyz>
|
|
||||||
copy-to = Sent
|
|
||||||
archive = Archive
|
|
||||||
postpone = Drafts
|
|
||||||
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
|
|
||||||
aliases = blue@liphlink.xyz,red@liphlink.xyz
|
|
||||||
|
|
||||||
[spam_notmuch]
|
|
||||||
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
|
||||||
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 = notmuch:///home/liph/Mail/proton
|
||||||
outgoing = smtp+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1026
|
query-map = ~/.config/aerc/notmuch-queries.conf
|
||||||
|
outgoing = smtp+insecure://liiph%40protonmail.com@127.0.0.1:1025
|
||||||
|
outgoing-cred-cmd = pass show mail/liiph@protonmail.com
|
||||||
default = INBOX
|
default = INBOX
|
||||||
from = Liph <spam@liphlink.xyz>
|
from = Liph <liiph@proton.me>
|
||||||
copy-to = Sent
|
copy-to = Sent
|
||||||
archive = Archive
|
archive = Archive
|
||||||
|
default = Inbox
|
||||||
postpone = Drafts
|
postpone = Drafts
|
||||||
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
|
|
||||||
aliases = liiph@proton.me,liiph@pm.me,ph.waibel@proton.me,ph.waibel@pm.me
|
aliases = liiph@proton.me,liiph@pm.me,ph.waibel@proton.me,ph.waibel@pm.me
|
||||||
|
|||||||
@@ -27,15 +27,9 @@ index-format=notmuch://~/.local/share/mail
|
|||||||
# the user home dir. When redirecting aerc's output to a file using > shell
|
# the user home dir. When redirecting aerc's output to a file using > shell
|
||||||
# redirection, this setting is ignored and log messages are printed to stdout.
|
# redirection, this setting is ignored and log messages are printed to stdout.
|
||||||
#
|
#
|
||||||
#log-file=
|
log-file=~/.local/share/aerc/aerc.log
|
||||||
|
log-level=debug
|
||||||
|
|
||||||
# Only log messages above the specified level to log-file. Supported levels
|
|
||||||
# are: trace, debug, info, warn and error. When redirecting aerc's output to
|
|
||||||
# a file using > shell redirection, this setting is ignored and the log level
|
|
||||||
# is forced to trace.
|
|
||||||
#
|
|
||||||
# Default: info
|
|
||||||
#log-level=info
|
|
||||||
|
|
||||||
# Disable IPC entirely. Don't run commands (including mailto:... and mbox:...)
|
# Disable IPC entirely. Don't run commands (including mailto:... and mbox:...)
|
||||||
# in an existing aerc instance, and don't start an IPC server to allow
|
# in an existing aerc instance, and don't start an IPC server to allow
|
||||||
@@ -95,10 +89,12 @@ index-format=notmuch://~/.local/share/mail
|
|||||||
# width specifier is set, '*' is used by default.
|
# width specifier is set, '*' is used by default.
|
||||||
#
|
#
|
||||||
# Default: flags:4,name<20%,subject,date>=
|
# Default: flags:4,name<20%,subject,date>=
|
||||||
#index-columns=flags:4,name<20%,subject,date>=
|
index-columns=flags:4,name<35%,subject,date>=
|
||||||
|
column-name={{if match .Folder "(?i)Sent|Gesendet"}}{{index (.To | persons) 0}}{{else}}{{index (.From | persons) 0}}{{end}}
|
||||||
border-char-vertical="│"
|
border-char-vertical="│"
|
||||||
border-char-horizontal="─"
|
border-char-horizontal="─"
|
||||||
styleset-name=catppuccin-mocha
|
styleset-name="gruvbox-dark"
|
||||||
|
threading-enabled=true
|
||||||
|
|
||||||
#
|
#
|
||||||
# Each name in index-columns must have a corresponding column-$name setting.
|
# Each name in index-columns must have a corresponding column-$name setting.
|
||||||
@@ -181,7 +177,7 @@ styleset-name=catppuccin-mocha
|
|||||||
# Width of the sidebar, including the border.
|
# Width of the sidebar, including the border.
|
||||||
#
|
#
|
||||||
# Default: 22
|
# Default: 22
|
||||||
sidebar-width=22
|
sidebar-width=34
|
||||||
|
|
||||||
#
|
#
|
||||||
# Default split layout for message list tabs. The syntax is:
|
# Default split layout for message list tabs. The syntax is:
|
||||||
@@ -251,13 +247,13 @@ mouse-enabled=false
|
|||||||
# See aerc-templates(7) for all available fields and functions.
|
# See aerc-templates(7) for all available fields and functions.
|
||||||
#
|
#
|
||||||
# Default: {{.Folder}}
|
# Default: {{.Folder}}
|
||||||
#dirlist-left={{.Folder}}
|
dirlist-left={{if match .Folder "/"}}{{.Style (index (.Folder | split "/" | tail 1) 0) "folder_sub"}}{{else}}{{.Folder}}{{end}}
|
||||||
|
|
||||||
# Template for the right side of the directory list.
|
# Template for the right side of the directory list.
|
||||||
# See aerc-templates(7) for all available fields and functions.
|
# See aerc-templates(7) for all available fields and functions.
|
||||||
#
|
#
|
||||||
# Default: {{if .Unread}}{{humanReadable .Unread}}{{end}}
|
# Default: {{if .Unread}}{{humanReadable .Unread}}{{end}}
|
||||||
#dirlist-right={{if .Unread}}{{humanReadable .Unread}}{{end}}
|
dirlist-right={{if .Unread}}{{humanReadable .Unread}} / {{end}}{{humanReadable .Exists}}
|
||||||
|
|
||||||
# Delay after which the messages are actually listed when entering a directory.
|
# Delay after which the messages are actually listed when entering a directory.
|
||||||
# This avoids loading messages when skipping over folders and makes the UI more
|
# This avoids loading messages when skipping over folders and makes the UI more
|
||||||
@@ -270,13 +266,13 @@ mouse-enabled=false
|
|||||||
# expand the folders.
|
# expand the folders.
|
||||||
#
|
#
|
||||||
# Default: false
|
# Default: false
|
||||||
#dirlist-tree=false
|
dirlist-tree=true
|
||||||
|
|
||||||
# If dirlist-tree is enabled, set level at which folders are collapsed by
|
# If dirlist-tree is enabled, set level at which folders are collapsed by
|
||||||
# default. Set to 0 to disable.
|
# default. Set to 0 to disable.
|
||||||
#
|
#
|
||||||
# Default: 0
|
# Default: 0
|
||||||
#dirlist-collapse=0
|
dirlist-collapse=1
|
||||||
|
|
||||||
# List of space-separated criteria to sort the messages by, see *sort*
|
# List of space-separated criteria to sort the messages by, see *sort*
|
||||||
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
|
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
|
||||||
@@ -576,11 +572,11 @@ sort = -r date
|
|||||||
|
|
||||||
[viewer]
|
[viewer]
|
||||||
# pager=less -Rc
|
# pager=less -Rc
|
||||||
pager=bat
|
pager= bat --style=plain --paging=always
|
||||||
html-unsafe-images=false
|
# html-unsafe-images=false
|
||||||
html-filter=dante
|
# html-filter=dante
|
||||||
image/*=chafa -f kitty -s ${width}x${height}
|
image/*=chafa -f kitty -s ${width}x${height}
|
||||||
text/html=dante
|
text/html=w3m -T text/html -dump -o display_link_number=1
|
||||||
application/pdf=zathura
|
application/pdf=zathura
|
||||||
video/*=mpv
|
video/*=mpv
|
||||||
header-layout=From,To,Subject,Date
|
header-layout=From,To,Subject,Date
|
||||||
@@ -637,9 +633,16 @@ header-layout=From,To,Subject,Date
|
|||||||
#parse-http-links=true
|
#parse-http-links=true
|
||||||
|
|
||||||
[compose]
|
[compose]
|
||||||
editor=nvim # or your preferred editor
|
editor=nvim +'set ft=mail.markdown'
|
||||||
|
|
||||||
|
# editor=nvim +setf\ mail
|
||||||
|
# editor=nvim # or your preferred editor
|
||||||
|
# Address book command
|
||||||
address-book-cmd=abook --mutt-query '%s'
|
address-book-cmd=abook --mutt-query '%s'
|
||||||
# Specifies the command to run the editor with. It will be shown in an embedded
|
|
||||||
|
# Auto-complete addresses
|
||||||
|
# address-book-cmd-match='%s'
|
||||||
|
# address-book-cmd=abook --mutt-query '%s'
|
||||||
# terminal, though it may also launch a graphical window if the environment
|
# terminal, though it may also launch a graphical window if the environment
|
||||||
# supports it. Defaults to $EDITOR, or vi.
|
# supports it. Defaults to $EDITOR, or vi.
|
||||||
#editor=
|
#editor=
|
||||||
@@ -697,7 +700,7 @@ edit-headers=true
|
|||||||
# standard output, one file per line. If it is present, then aerc does not
|
# standard output, one file per line. If it is present, then aerc does not
|
||||||
# capture the standard output and instead reads the files from the temporary
|
# capture the standard output and instead reads the files from the temporary
|
||||||
# file which should have the same format.
|
# file which should have the same format.
|
||||||
#file-picker-cmd=
|
#file-picker-cmd=/home/liph/.local/bin/yazi-picker %f
|
||||||
|
|
||||||
#
|
#
|
||||||
# Allow to address yourself when replying
|
# Allow to address yourself when replying
|
||||||
@@ -729,18 +732,10 @@ edit-headers=true
|
|||||||
# text. To actually make use of this format's features, you'll need support in
|
# text. To actually make use of this format's features, you'll need support in
|
||||||
# your editor.
|
# your editor.
|
||||||
#
|
#
|
||||||
#format-flowed=false
|
format-flowed=false
|
||||||
|
|
||||||
[multipart-converters]
|
[multipart-converters]
|
||||||
#
|
text/html=/home/liph/.local/bin/aerc-md-convert
|
||||||
# Converters allow to generate multipart/alternative messages by converting the
|
|
||||||
# main text/plain part into any other MIME type. Only exact MIME types are
|
|
||||||
# accepted. The commands are invoked with sh -c and are expected to output
|
|
||||||
# valid UTF-8 text.
|
|
||||||
#
|
|
||||||
# Example (obviously, this requires that you write your main text/plain body
|
|
||||||
# using the markdown syntax):
|
|
||||||
#text/html=pandoc -f markdown -t html --standalone
|
|
||||||
|
|
||||||
[filters]
|
[filters]
|
||||||
#
|
#
|
||||||
@@ -782,9 +777,7 @@ text/plain=colorize
|
|||||||
text/calendar=calendar
|
text/calendar=calendar
|
||||||
message/delivery-status=colorize
|
message/delivery-status=colorize
|
||||||
message/rfc822=colorize
|
message/rfc822=colorize
|
||||||
#text/html=pandoc -f html -t plain | colorize
|
text/html=w3m -T text/html -dump -o display_link_number=1
|
||||||
text/html=! html
|
|
||||||
text/html=! w3m -T text/html -I UTF-8
|
|
||||||
#text/*=bat -fP --file-name="$AERC_FILENAME"
|
#text/*=bat -fP --file-name="$AERC_FILENAME"
|
||||||
#application/x-sh=bat -fP -l sh
|
#application/x-sh=bat -fP -l sh
|
||||||
#image/*=catimg -w $(tput cols) -
|
#image/*=catimg -w $(tput cols) -
|
||||||
@@ -818,12 +811,12 @@ text/html=! w3m -T text/html -I UTF-8
|
|||||||
# text/html=surf -dfgms
|
# text/html=surf -dfgms
|
||||||
# text/plain=gvim {} +125
|
# text/plain=gvim {} +125
|
||||||
# message/rfc822=thunderbird
|
# message/rfc822=thunderbird
|
||||||
|
text/html=w3m
|
||||||
[hooks]
|
|
||||||
# text/html=librewolf
|
|
||||||
x-scheme-handler/http=librewolf
|
x-scheme-handler/http=librewolf
|
||||||
x-scheme-handler/https=librewolf
|
x-scheme-handler/https=librewolf
|
||||||
|
text/calendar=khal import --batch
|
||||||
|
|
||||||
|
[hooks]
|
||||||
#
|
#
|
||||||
# Hooks are triggered whenever the associated event occurs.
|
# Hooks are triggered whenever the associated event occurs.
|
||||||
|
|
||||||
@@ -888,3 +881,6 @@ template-dirs=${XDG_CONFIG_HOME:-~/.config}/aerc/templates
|
|||||||
#
|
#
|
||||||
# default: forward_as_body
|
# default: forward_as_body
|
||||||
#forwards=forward_as_body
|
#forwards=forward_as_body
|
||||||
|
|
||||||
|
[terminal]
|
||||||
|
passthrough=false
|
||||||
|
|||||||
@@ -0,0 +1,883 @@
|
|||||||
|
#
|
||||||
|
# aerc main configuration
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# Used as a default path for save operations if no other path is specified.
|
||||||
|
# ~ is expanded to the current user home dir.
|
||||||
|
#
|
||||||
|
#default-save-path=
|
||||||
|
|
||||||
|
# If set to "gpg", aerc will use system gpg binary and keystore for all crypto
|
||||||
|
# operations. If set to "internal", the internal openpgp keyring will be used.
|
||||||
|
# If set to "auto", the system gpg will be preferred unless the internal
|
||||||
|
# keyring already exists, in which case the latter will be used.
|
||||||
|
#
|
||||||
|
# Default: auto
|
||||||
|
#pgp-provider=auto
|
||||||
|
|
||||||
|
# By default, the file permissions of accounts.conf must be restrictive and
|
||||||
|
# only allow reading by the file owner (0600). Set this option to true to
|
||||||
|
# ignore this permission check. Use this with care as it may expose your
|
||||||
|
# credentials.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
unsafe-accounts-conf=false
|
||||||
|
index-format=notmuch://~/.local/share/mail
|
||||||
|
# Output log messages to specified file. A path starting with ~/ is expanded to
|
||||||
|
# the user home dir. When redirecting aerc's output to a file using > shell
|
||||||
|
# redirection, this setting is ignored and log messages are printed to stdout.
|
||||||
|
#
|
||||||
|
log-file=~/.local/share/aerc/aerc.log
|
||||||
|
log-level=debug
|
||||||
|
|
||||||
|
|
||||||
|
# Disable IPC entirely. Don't run commands (including mailto:... and mbox:...)
|
||||||
|
# in an existing aerc instance, and don't start an IPC server to allow
|
||||||
|
# subsequent aerc instances to run commands in the current one.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#disable-ipc=false
|
||||||
|
|
||||||
|
# Don't run mailto:... commands over IPC; start a new aerc instance with the
|
||||||
|
# composer instead.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#disable-ipc-mailto=false
|
||||||
|
#
|
||||||
|
# Don't run mbox:... commands over IPC; start a new aerc instance with the mbox
|
||||||
|
# file instead.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#disable-ipc-mbox=false
|
||||||
|
|
||||||
|
# Set the $TERM environment variable used for the embedded terminal.
|
||||||
|
#
|
||||||
|
# Default: xterm-256color
|
||||||
|
#term=xterm-256color
|
||||||
|
|
||||||
|
# Display OSC8 strings in the embedded terminal
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#enable-osc8=false
|
||||||
|
|
||||||
|
# Default shell command to use for :menu. This will be executed with sh -c and
|
||||||
|
# will run in an popover dialog.
|
||||||
|
#
|
||||||
|
# Any occurrence of %f will be replaced by a temporary file path where the
|
||||||
|
# command is expected to write output lines to be consumed by :menu. Otherwise,
|
||||||
|
# the lines will be read from the command's standard output.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# default-menu-cmd=fzf
|
||||||
|
# default-menu-cmd=fzf --multi
|
||||||
|
# default-menu-cmd=dmenu -l 20
|
||||||
|
# default-menu-cmd=ranger --choosefiles=%f
|
||||||
|
#
|
||||||
|
#default-menu-cmd=
|
||||||
|
|
||||||
|
[ui]
|
||||||
|
#
|
||||||
|
# Describes the format for each row in a mailbox view. This is a comma
|
||||||
|
# separated list of column names with an optional align and width suffix. After
|
||||||
|
# the column name, one of the '<' (left), ':' (center) or '>' (right) alignment
|
||||||
|
# characters can be added (by default, left) followed by an optional width
|
||||||
|
# specifier. The width is either an integer representing a fixed number of
|
||||||
|
# characters, or a percentage between 1% and 99% representing a fraction of the
|
||||||
|
# terminal width. It can also be one of the '*' (auto) or '=' (fit) special
|
||||||
|
# width specifiers. Auto width columns will be equally attributed the remaining
|
||||||
|
# terminal width. Fit width columns take the width of their contents. If no
|
||||||
|
# width specifier is set, '*' is used by default.
|
||||||
|
#
|
||||||
|
# Default: flags:4,name<20%,subject,date>=
|
||||||
|
index-columns=flags:4,name<35%,subject,date>=
|
||||||
|
column-name={{if match .Folder "(?i)Sent|Gesendet"}}{{index (.To | persons) 0}}{{else}}{{index (.From | persons) 0}}{{end}}
|
||||||
|
border-char-vertical="│"
|
||||||
|
border-char-horizontal="─"
|
||||||
|
styleset-name="gruvbox-dark"
|
||||||
|
threading-enabled=true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Each name in index-columns must have a corresponding column-$name setting.
|
||||||
|
# All column-$name settings accept golang text/template syntax. See
|
||||||
|
# aerc-templates(7) for available template attributes and functions.
|
||||||
|
#
|
||||||
|
# Here are some examples to show the To field instead of the From field for
|
||||||
|
# an email (modifying column-name):
|
||||||
|
#
|
||||||
|
# 1. a generic one
|
||||||
|
# column-name={{ .Peer | names | join ", " }}
|
||||||
|
# 2. based upon the selected folder
|
||||||
|
# column-name={{if match .Folder "^(Gesendet|Sent)$"}}{{index (.To | names) 0}}{{else}}{{index (.From | names) 0}}{{end}}
|
||||||
|
#
|
||||||
|
# Default settings
|
||||||
|
#column-flags={{.Flags | join ""}}
|
||||||
|
#column-name={{index (.From | names) 0}}
|
||||||
|
#column-subject={{.ThreadPrefix}}{{.Subject}}
|
||||||
|
#column-date={{.DateAutoFormat .Date.Local}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# String separator inserted between columns. When the column width specifier is
|
||||||
|
# an exact number of characters, the separator is added to it (i.e. the exact
|
||||||
|
# width will be fully available for the column contents).
|
||||||
|
#
|
||||||
|
# Default: " "
|
||||||
|
#column-separator=" "
|
||||||
|
|
||||||
|
#
|
||||||
|
# See time.Time#Format at https://godoc.org/time#Time.Format
|
||||||
|
#
|
||||||
|
# Default: 2006 Jan 02
|
||||||
|
#timestamp-format=2006 Jan 02
|
||||||
|
|
||||||
|
#
|
||||||
|
# Index-only time format for messages that were received/sent today.
|
||||||
|
# If this is empty, timestamp-format is used instead.
|
||||||
|
#
|
||||||
|
# Default: 15:04
|
||||||
|
#this-day-time-format=15:04
|
||||||
|
|
||||||
|
#
|
||||||
|
# Index-only time format for messages that were received/sent within the last
|
||||||
|
# 7 days. If this is empty, timestamp-format is used instead.
|
||||||
|
#
|
||||||
|
# Default: Jan 02
|
||||||
|
#this-week-time-format=Jan 02
|
||||||
|
|
||||||
|
#
|
||||||
|
# Index-only time format for messages that were received/sent this year.
|
||||||
|
# If this is empty, timestamp-format is used instead.
|
||||||
|
#
|
||||||
|
#Default: Jan 02
|
||||||
|
#this-year-time-format=Jan 02
|
||||||
|
|
||||||
|
#
|
||||||
|
# Overrides timestamp-format for the message view.
|
||||||
|
#
|
||||||
|
# Default: 2006 Jan 02, 15:04 GMT-0700
|
||||||
|
#message-view-timestamp-format=2006 Jan 02, 15:04 GMT-0700
|
||||||
|
|
||||||
|
#
|
||||||
|
# If set, overrides timestamp-format in the message view for messages
|
||||||
|
# that were received/sent today.
|
||||||
|
#
|
||||||
|
#message-view-this-day-time-format=
|
||||||
|
|
||||||
|
# If set, overrides timestamp-format in the message view for messages
|
||||||
|
# that were received/sent within the last 7 days.
|
||||||
|
#
|
||||||
|
#message-view-this-week-time-format=
|
||||||
|
|
||||||
|
#
|
||||||
|
# If set, overrides *timestamp-format* in the message view for messages
|
||||||
|
# that were received/sent this year.
|
||||||
|
#
|
||||||
|
#message-view-this-year-time-format=
|
||||||
|
|
||||||
|
#
|
||||||
|
# Width of the sidebar, including the border.
|
||||||
|
#
|
||||||
|
# Default: 22
|
||||||
|
sidebar-width=22
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default split layout for message list tabs. The syntax is:
|
||||||
|
#
|
||||||
|
# [<direction>] <size>
|
||||||
|
#
|
||||||
|
# <direction> is optional and defaults to horizontal. It can take one
|
||||||
|
# of the following values: h, horiz, horizontal, v, vert, vertical.
|
||||||
|
#
|
||||||
|
# <size> is a positive integer representing the size (in terminal cells)
|
||||||
|
# of the message list window.
|
||||||
|
#
|
||||||
|
#message-list-split=
|
||||||
|
|
||||||
|
#
|
||||||
|
# Message to display when viewing an empty folder.
|
||||||
|
#
|
||||||
|
# Default: (no messages)
|
||||||
|
#empty-message=(no messages)
|
||||||
|
|
||||||
|
# Message to display when no folders exists or are all filtered
|
||||||
|
#
|
||||||
|
# Default: (no folders)
|
||||||
|
#empty-dirlist=(no folders)
|
||||||
|
#
|
||||||
|
# Value to set {{.Subject}} template to when subject is empty.
|
||||||
|
#
|
||||||
|
# Default: (no subject)
|
||||||
|
#empty-subject=(no subject)
|
||||||
|
|
||||||
|
# Enable mouse events in the ui, e.g. clicking and scrolling with the mousewheel
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
mouse-enabled=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ring the bell when new messages are received
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#new-message-bell=true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template to use for Account tab titles
|
||||||
|
#
|
||||||
|
# Default: {{.Account}}
|
||||||
|
#tab-title-account={{.Account}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template to use for Composer tab titles
|
||||||
|
#
|
||||||
|
# Default: {{if .To}}to:{{index (.To | shortmboxes) 0}} {{end}}{{.SubjectBase}}
|
||||||
|
#tab-title-composer={{if .To}}to:{{index (.To | shortmboxes) 0}} {{end}}{{.SubjectBase}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template to use for Message Viewer tab titles
|
||||||
|
#
|
||||||
|
# Default: {{.Subject}}
|
||||||
|
#tab-title-viewer={{.Subject}}
|
||||||
|
|
||||||
|
|
||||||
|
# Marker to show before a pinned tab's name.
|
||||||
|
#
|
||||||
|
# Default: `
|
||||||
|
#pinned-tab-marker='`'
|
||||||
|
|
||||||
|
# Template for the left side of the directory list.
|
||||||
|
# See aerc-templates(7) for all available fields and functions.
|
||||||
|
#
|
||||||
|
# Default: {{.Folder}}
|
||||||
|
#dirlist-left={{.Folder}}
|
||||||
|
|
||||||
|
# Template for the right side of the directory list.
|
||||||
|
# See aerc-templates(7) for all available fields and functions.
|
||||||
|
#
|
||||||
|
# Default: {{if .Unread}}{{humanReadable .Unread}}{{end}}
|
||||||
|
#dirlist-right={{if .Unread}}{{humanReadable .Unread}}{{end}}
|
||||||
|
|
||||||
|
# Delay after which the messages are actually listed when entering a directory.
|
||||||
|
# This avoids loading messages when skipping over folders and makes the UI more
|
||||||
|
# responsive. If you do not want that, set it to 0s.
|
||||||
|
#
|
||||||
|
# Default: 200ms
|
||||||
|
#dirlist-delay=200ms
|
||||||
|
|
||||||
|
# Display the directory list as a foldable tree that allows to collapse and
|
||||||
|
# expand the folders.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
dirlist-tree=true
|
||||||
|
|
||||||
|
# If dirlist-tree is enabled, set level at which folders are collapsed by
|
||||||
|
# default. Set to 0 to disable.
|
||||||
|
#
|
||||||
|
# Default: 0
|
||||||
|
dirlist-collapse=1
|
||||||
|
|
||||||
|
# List of space-separated criteria to sort the messages by, see *sort*
|
||||||
|
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
|
||||||
|
# reverses that criterion.
|
||||||
|
#
|
||||||
|
# Example: "from -r date"
|
||||||
|
#
|
||||||
|
sort = -r date
|
||||||
|
|
||||||
|
# Moves to next message when the current message is deleted
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#next-message-on-delete=true
|
||||||
|
|
||||||
|
# Automatically set the "seen" flag when a message is opened in the message
|
||||||
|
# viewer.
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#auto-mark-read=true
|
||||||
|
|
||||||
|
# The directories where the stylesets are stored. It takes a colon-separated
|
||||||
|
# list of directories. If this is unset or if a styleset cannot be found, the
|
||||||
|
# following paths will be used as a fallback in that order:
|
||||||
|
#
|
||||||
|
# ${XDG_CONFIG_HOME:-~/.config}/aerc/stylesets
|
||||||
|
# ${XDG_DATA_HOME:-~/.local/share}/aerc/stylesets
|
||||||
|
# /usr/local/share/aerc/stylesets
|
||||||
|
# /usr/share/aerc/stylesets
|
||||||
|
#
|
||||||
|
#stylesets-dirs=
|
||||||
|
|
||||||
|
# Uncomment to use box-drawing characters for vertical and horizontal borders.
|
||||||
|
#
|
||||||
|
# Default: "│" and "─"
|
||||||
|
#border-char-vertical="│"
|
||||||
|
#border-char-horizontal="─"
|
||||||
|
|
||||||
|
# Sets the styleset to use for the aerc ui elements.
|
||||||
|
#
|
||||||
|
# Default: default
|
||||||
|
#styleset-name=default
|
||||||
|
|
||||||
|
# Activates fuzzy search in commands and their arguments: the typed string is
|
||||||
|
# searched in the command or option in any position, and need not be
|
||||||
|
# consecutive characters in the command or option.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#fuzzy-complete=false
|
||||||
|
|
||||||
|
# How long to wait after the last input before auto-completion is triggered.
|
||||||
|
#
|
||||||
|
# Default: 250ms
|
||||||
|
#completion-delay=250ms
|
||||||
|
|
||||||
|
# The minimum required characters to allow auto-completion to be triggered after
|
||||||
|
# completion-delay.
|
||||||
|
#
|
||||||
|
# Setting this to "manual" disables automatic completion, leaving only the
|
||||||
|
# manually triggered completion with the $complete key (see aerc-binds(5) for
|
||||||
|
# more details).
|
||||||
|
#
|
||||||
|
# Default: 1
|
||||||
|
#completion-min-chars=1
|
||||||
|
|
||||||
|
#
|
||||||
|
# Global switch for completion popovers
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#completion-popovers=true
|
||||||
|
|
||||||
|
# Uncomment to use UTF-8 symbols to indicate PGP status of messages
|
||||||
|
#
|
||||||
|
# Default: ASCII
|
||||||
|
#icon-unencrypted=
|
||||||
|
#icon-encrypted=✔
|
||||||
|
#icon-signed=✔
|
||||||
|
#icon-signed-encrypted=✔
|
||||||
|
#icon-unknown=✘
|
||||||
|
#icon-invalid=⚠
|
||||||
|
|
||||||
|
# Reverses the order of the message list. By default, the message list is
|
||||||
|
# ordered with the newest (highest UID) message on top. Reversing the order
|
||||||
|
# will put the oldest (lowest UID) message on top. This can be useful in cases
|
||||||
|
# where the backend does not support sorting.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#reverse-msglist-order = false
|
||||||
|
|
||||||
|
# Reverse display of the message threads. Default order is the initial
|
||||||
|
# message is on the top with all the replies being displayed below. The
|
||||||
|
# reverse option will put the initial message at the bottom with the
|
||||||
|
# replies on top.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#reverse-thread-order=false
|
||||||
|
|
||||||
|
# Positions the cursor on the last message in the message list (at the
|
||||||
|
# bottom of the view) when opening a new folder.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#select-last-message=false
|
||||||
|
|
||||||
|
# Sort the thread siblings according to the sort criteria for the messages. If
|
||||||
|
# sort-thread-siblings is false, the thread siblings will be sorted based on
|
||||||
|
# the message UID in ascending order. This option is only applicable for
|
||||||
|
# client-side threading with a backend that enables sorting. Note that there's
|
||||||
|
# a performance impact when sorting is activated.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#sort-thread-siblings=false
|
||||||
|
|
||||||
|
# Set the scroll offset in number of lines from the top and bottom of the
|
||||||
|
# message list.
|
||||||
|
#
|
||||||
|
# Default: 0
|
||||||
|
#msglist-scroll-offset = 0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Enable a threaded view of messages. If this is not supported by the backend
|
||||||
|
# (IMAP server or notmuch), threads will be built by the client.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#threading-enabled=false
|
||||||
|
|
||||||
|
# Force client-side thread building
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#force-client-threads=false
|
||||||
|
|
||||||
|
# If no References nor In-Reply-To headers can be matched to build client side
|
||||||
|
# threads, fallback to similar subjects.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#threading-by-subject=false
|
||||||
|
|
||||||
|
# Show thread context enables messages which do not match the current query (or
|
||||||
|
# belong to the current mailbox) to be shown for context. These messages can be
|
||||||
|
# styled separately using "msglist_thread_context" in a styleset. This feature
|
||||||
|
# is not supported by all backends
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#show-thread-context=false
|
||||||
|
|
||||||
|
# Debounce client-side thread building
|
||||||
|
#
|
||||||
|
# Default: 50ms
|
||||||
|
#client-threads-delay=50ms
|
||||||
|
|
||||||
|
#
|
||||||
|
# Thread prefix customization:
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the arrow head.
|
||||||
|
#
|
||||||
|
# Default: ">"
|
||||||
|
#thread-prefix-tip = ">"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the arrow indentation.
|
||||||
|
#
|
||||||
|
# Default: " "
|
||||||
|
#thread-prefix-indent = " "
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the vertical extension of
|
||||||
|
# the arrow.
|
||||||
|
#
|
||||||
|
# Default: "│"
|
||||||
|
#thread-prefix-stem = "│"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the horizontal extension
|
||||||
|
# of the arrow.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-limb = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the folded thread
|
||||||
|
# indicator.
|
||||||
|
#
|
||||||
|
# Default: "+"
|
||||||
|
#thread-prefix-folded = "+"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the unfolded thread
|
||||||
|
# indicator.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-unfolded = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the first child connector.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-first-child = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the connector used if
|
||||||
|
# the message has siblings.
|
||||||
|
#
|
||||||
|
# Default: "├─"
|
||||||
|
#thread-prefix-has-siblings = "├─"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the connector used if the
|
||||||
|
# message has no parents and no children.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-lone = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the connector used if the
|
||||||
|
# message has no parents and has children.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-orphan = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the connector for the last
|
||||||
|
# sibling.
|
||||||
|
#
|
||||||
|
# Default: "└─"
|
||||||
|
#thread-prefix-last-sibling = "└─"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the reversed thread prefix appearance by selecting the connector for
|
||||||
|
# the last sibling.
|
||||||
|
#
|
||||||
|
# Default: "┌─"
|
||||||
|
#thread-prefix-last-sibling-reverse = "┌─"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the thread prefix appearance by selecting the connector for dummy
|
||||||
|
# thread.
|
||||||
|
#
|
||||||
|
# Default: "┬─"
|
||||||
|
#thread-prefix-dummy = "┬─"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the reversed thread prefix appearance by selecting the connector for
|
||||||
|
# dummy thread.
|
||||||
|
#
|
||||||
|
# Default: "┴─"
|
||||||
|
#thread-prefix-dummy-reverse = "┴─"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the reversed thread prefix appearance by selecting the first child
|
||||||
|
# connector.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-first-child-reverse = ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Customize the reversed thread prefix appearance by selecting the connector
|
||||||
|
# used if the message has no parents and has children.
|
||||||
|
#
|
||||||
|
# Default: ""
|
||||||
|
#thread-prefix-orphan-reverse = ""
|
||||||
|
|
||||||
|
[statusline]
|
||||||
|
#
|
||||||
|
# Describes the format for the status line. This is a comma separated list of
|
||||||
|
# column names with an optional align and width suffix. See [ui].index-columns
|
||||||
|
# for more details. To completely mute the status line except for push
|
||||||
|
# notifications, explicitly set status-columns to an empty string.
|
||||||
|
#
|
||||||
|
# Default: left<*,center:=,right>*
|
||||||
|
#status-columns=left<*,center:=,right>*
|
||||||
|
|
||||||
|
#
|
||||||
|
# Each name in status-columns must have a corresponding column-$name setting.
|
||||||
|
# All column-$name settings accept golang text/template syntax. See
|
||||||
|
# aerc-templates(7) for available template attributes and functions.
|
||||||
|
#
|
||||||
|
# Default settings
|
||||||
|
#column-left=[{{.Account}}] {{.StatusInfo}}
|
||||||
|
#column-center={{.PendingKeys}}
|
||||||
|
#column-right={{.TrayInfo}} | {{cwd}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# String separator inserted between columns.
|
||||||
|
# See [ui].column-separator for more details.
|
||||||
|
#
|
||||||
|
#column-separator=" "
|
||||||
|
|
||||||
|
# Specifies the separator between grouped statusline elements.
|
||||||
|
#
|
||||||
|
# Default: " | "
|
||||||
|
#separator=" | "
|
||||||
|
|
||||||
|
# Defines the mode for displaying the status elements.
|
||||||
|
# Options: text, icon
|
||||||
|
#
|
||||||
|
# Default: text
|
||||||
|
#display-mode=text
|
||||||
|
|
||||||
|
[viewer]
|
||||||
|
# pager=less -Rc
|
||||||
|
pager= bat --style=plain --paging=always
|
||||||
|
# html-unsafe-images=false
|
||||||
|
# html-filter=dante
|
||||||
|
image/*=chafa -f kitty -s ${width}x${height}
|
||||||
|
text/html=w3m -T text/html -dump -o display_link_number=1
|
||||||
|
application/pdf=zathura
|
||||||
|
video/*=mpv
|
||||||
|
header-layout=From,To,Subject,Date
|
||||||
|
|
||||||
|
|
||||||
|
#w3m -dump -T text/html -o display_link_number=1
|
||||||
|
#
|
||||||
|
# Specifies the pager to use when displaying emails. Note that some filters
|
||||||
|
# may add ANSI codes to add color to rendered emails, so you may want to use a
|
||||||
|
# pager which supports ANSI codes.
|
||||||
|
#
|
||||||
|
# Default: less -Rc
|
||||||
|
|
||||||
|
#
|
||||||
|
# If an email offers several versions (multipart), you can configure which
|
||||||
|
# mimetype to prefer. For example, this can be used to prefer plaintext over
|
||||||
|
# html emails.
|
||||||
|
#
|
||||||
|
# Default: text/plain,text/html
|
||||||
|
#alternatives=text/plain,text/html
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default setting to determine whether to show full headers or only parsed
|
||||||
|
# ones in message viewer.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#show-headers=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Layout of headers when viewing a message. To display multiple headers in the
|
||||||
|
# same row, separate them with a pipe, e.g. "From|To". Rows will be hidden if
|
||||||
|
# none of their specified headers are present in the message.
|
||||||
|
#
|
||||||
|
# Default: From|To,Cc|Bcc,Date,Subject
|
||||||
|
#header-layout=From|To,Cc|Bcc,Date,Subject
|
||||||
|
|
||||||
|
# Whether to always show the mimetype of an email, even when it is just a single part
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#always-show-mime=false
|
||||||
|
|
||||||
|
# Define the maximum height of the mimetype switcher before a scrollbar is used.
|
||||||
|
# The height of the mimetype switcher is restricted to half of the display
|
||||||
|
# height. If the provided value for the height is zero, the number of parts will
|
||||||
|
# be used as the height of the type switcher.
|
||||||
|
#
|
||||||
|
# Default: 0
|
||||||
|
#max-mime-height = 0
|
||||||
|
|
||||||
|
# Parses and extracts http links when viewing a message. Links can then be
|
||||||
|
# accessed with the open-link command.
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#parse-http-links=true
|
||||||
|
|
||||||
|
[compose]
|
||||||
|
editor=nvim +'set ft=mail.markdown'
|
||||||
|
|
||||||
|
# editor=nvim +setf\ mail
|
||||||
|
# editor=nvim # or your preferred editor
|
||||||
|
# Address book command
|
||||||
|
address-book-cmd=abook --mutt-query '%s'
|
||||||
|
|
||||||
|
# Auto-complete addresses
|
||||||
|
# address-book-cmd-match='%s'
|
||||||
|
# address-book-cmd=abook --mutt-query '%s'
|
||||||
|
# terminal, though it may also launch a graphical window if the environment
|
||||||
|
# supports it. Defaults to $EDITOR, or vi.
|
||||||
|
#editor=
|
||||||
|
|
||||||
|
#
|
||||||
|
# When set, aerc will create and read .eml files for composing that have
|
||||||
|
# non-standard \n linebreaks. This is only relevant if the used editor does not
|
||||||
|
# support CRLF linebreaks.
|
||||||
|
#
|
||||||
|
#lf-editor=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default header fields to display when composing a message. To display
|
||||||
|
# multiple headers in the same row, separate them with a pipe, e.g. "To|From".
|
||||||
|
#
|
||||||
|
# Default: To|From,Subject
|
||||||
|
#header-layout=To|From,Subject
|
||||||
|
|
||||||
|
#
|
||||||
|
# Edit headers into the text editor instead than separate fields.
|
||||||
|
#
|
||||||
|
# When this is true, address-book-cmd is not supported and address completion
|
||||||
|
# is left to the editor itself. Also, displaying multiple headers on the same
|
||||||
|
# line is not possible.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
edit-headers=true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sets focus to the email body when the composer window opens.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#focus-body=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Specifies the command to be used to tab-complete email addresses. Any
|
||||||
|
# occurrence of "%s" in the address-book-cmd will be replaced with what the
|
||||||
|
# user has typed so far.
|
||||||
|
#
|
||||||
|
# The command must output the completions to standard output, one completion
|
||||||
|
# per line. Each line must be tab-delimited, with an email address occurring as
|
||||||
|
# the first field. Only the email address field is required. The second field,
|
||||||
|
# if present, will be treated as the contact name. Additional fields are
|
||||||
|
# ignored.
|
||||||
|
#
|
||||||
|
# This parameter can also be set per account in accounts.conf.
|
||||||
|
#address-book-cmd=
|
||||||
|
|
||||||
|
# Specifies the command to be used to select attachments. Any occurrence of
|
||||||
|
# '%s' in the file-picker-cmd will be replaced with the argument <arg>
|
||||||
|
# to :attach -m <arg>. Any occurrence of '%f' will be replaced by the
|
||||||
|
# location of a temporary file, from which aerc will read the selected files.
|
||||||
|
#
|
||||||
|
# If '%f' is not present, the command must output the selected files to
|
||||||
|
# standard output, one file per line. If it is present, then aerc does not
|
||||||
|
# capture the standard output and instead reads the files from the temporary
|
||||||
|
# file which should have the same format.
|
||||||
|
#file-picker-cmd=/home/liph/.local/bin/yazi-picker %f
|
||||||
|
|
||||||
|
#
|
||||||
|
# Allow to address yourself when replying
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
#reply-to-self=true
|
||||||
|
|
||||||
|
# Warn before sending an email with an empty subject.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#empty-subject-warning=false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Warn before sending an email that matches the specified regexp but does not
|
||||||
|
# have any attachments. Leave empty to disable this feature.
|
||||||
|
#
|
||||||
|
# Uses Go's regexp syntax, documented at https://golang.org/s/re2syntax. The
|
||||||
|
# "(?im)" flags are set by default (case-insensitive and multi-line).
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# no-attachment-warning=^[^>]*attach(ed|ment)
|
||||||
|
#
|
||||||
|
#no-attachment-warning=
|
||||||
|
|
||||||
|
#
|
||||||
|
# When set, aerc will generate "format=flowed" bodies with a content type of
|
||||||
|
# "text/plain; format=flowed" as described in RFC3676. This format is easier to
|
||||||
|
# handle for some mailing software, and generally just looks like ordinary
|
||||||
|
# text. To actually make use of this format's features, you'll need support in
|
||||||
|
# your editor.
|
||||||
|
#
|
||||||
|
format-flowed=false
|
||||||
|
|
||||||
|
[multipart-converters]
|
||||||
|
text/html=/home/liph/.local/bin/aerc-md-convert
|
||||||
|
|
||||||
|
[filters]
|
||||||
|
#
|
||||||
|
text/plain=sed 's/^$//'
|
||||||
|
|
||||||
|
# Filters allow you to pipe an email body through a shell command to render
|
||||||
|
# certain emails differently, e.g. highlighting them with ANSI escape codes.
|
||||||
|
#
|
||||||
|
# The commands are invoked with sh -c. The following folders are prepended to
|
||||||
|
# the system $PATH to allow referencing filters from their name only:
|
||||||
|
#
|
||||||
|
# ${XDG_CONFIG_HOME:-~/.config}/aerc/filters
|
||||||
|
# ~/.local/libexec/aerc/filters
|
||||||
|
# ${XDG_DATA_HOME:-~/.local/share}/aerc/filters
|
||||||
|
# $PREFIX/libexec/aerc/filters
|
||||||
|
# $PREFIX/share/aerc/filters
|
||||||
|
# /usr/libexec/aerc/filters
|
||||||
|
# /usr/share/aerc/filters
|
||||||
|
#
|
||||||
|
# If you want to run a program in your default $PATH which has the same name
|
||||||
|
# as a builtin filter (e.g. /usr/bin/colorize), use its absolute path.
|
||||||
|
#
|
||||||
|
# The following variables are defined in the filter command environment:
|
||||||
|
#
|
||||||
|
# AERC_MIME_TYPE the part MIME type/subtype
|
||||||
|
# AERC_FORMAT the part content type format= parameter
|
||||||
|
# AERC_FILENAME the attachment filename (if any)
|
||||||
|
# AERC_SUBJECT the message Subject header value
|
||||||
|
# AERC_FROM the message From header value
|
||||||
|
#
|
||||||
|
# The first filter which matches the email's mimetype will be used, so order
|
||||||
|
# them from most to least specific.
|
||||||
|
#
|
||||||
|
# You can also match on non-mimetypes, by prefixing with the header to match
|
||||||
|
# against (non-case-sensitive) and a comma, e.g. subject,text will match a
|
||||||
|
# subject which contains "text". Use header,~regex to match against a regex.
|
||||||
|
#
|
||||||
|
text/plain=colorize
|
||||||
|
text/calendar=calendar
|
||||||
|
message/delivery-status=colorize
|
||||||
|
message/rfc822=colorize
|
||||||
|
text/html=w3m -T text/html -dump -o display_link_number=1
|
||||||
|
#text/*=bat -fP --file-name="$AERC_FILENAME"
|
||||||
|
#application/x-sh=bat -fP -l sh
|
||||||
|
#image/*=catimg -w $(tput cols) -
|
||||||
|
#subject,~Git(hub|lab)=lolcat -f
|
||||||
|
#from,thatguywhodoesnothardwraphismessages=wrap -w 100 | colorize
|
||||||
|
|
||||||
|
# This special filter is only used to post-process email headers when
|
||||||
|
# [viewer].show-headers=true
|
||||||
|
# By default, headers are piped directly into the pager.
|
||||||
|
#
|
||||||
|
.headers=colorize
|
||||||
|
|
||||||
|
[openers]
|
||||||
|
#
|
||||||
|
# Openers allow you to specify the command to use for the :open and :open-link
|
||||||
|
# actions on a per-MIME-type basis. The :open-link URL scheme is used to
|
||||||
|
# determine the MIME type as follows: x-scheme-handler/<scheme>.
|
||||||
|
#
|
||||||
|
# {} is expanded as the temporary filename or URL to be opened with proper
|
||||||
|
# shell quoting. If it is not encountered in the command, the filename/URL will
|
||||||
|
# be appended to the end of the command. The command will then be executed with
|
||||||
|
# `sh -c`.
|
||||||
|
#
|
||||||
|
# Like [filters], openers support basic shell globbing. The first opener which
|
||||||
|
# matches the part's MIME type (or URL scheme handler MIME type) will be used,
|
||||||
|
# so order them from most to least specific.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# x-scheme-handler/irc=hexchat
|
||||||
|
# x-scheme-handler/http*=printf '%s' {} | wl-copy
|
||||||
|
# text/html=surf -dfgms
|
||||||
|
# text/plain=gvim {} +125
|
||||||
|
# message/rfc822=thunderbird
|
||||||
|
text/html=w3m
|
||||||
|
x-scheme-handler/http=librewolf
|
||||||
|
x-scheme-handler/https=librewolf
|
||||||
|
text/calendar=khal import --batch
|
||||||
|
|
||||||
|
[hooks]
|
||||||
|
#
|
||||||
|
# Hooks are triggered whenever the associated event occurs.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when a new email arrives in the selected folder
|
||||||
|
#mail-received=notify-send "[$AERC_ACCOUNT/$AERC_FOLDER] New mail from $AERC_FROM_NAME" "$AERC_SUBJECT"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when mail is deleted from a folder
|
||||||
|
#mail-deleted=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when aerc adds mail to a folder
|
||||||
|
#mail-added=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when aerc starts
|
||||||
|
#aerc-startup=aerc :terminal calcurse && aerc :next-tab
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when aerc shuts down.
|
||||||
|
#aerc-shutdown=
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when notmuch tags are modified.
|
||||||
|
#tag-modified=
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executed when flags are changed on a message.
|
||||||
|
#flag-changed=mbsync "$AERC_ACCOUNT:$AERC_FOLDER" &
|
||||||
|
|
||||||
|
[templates]
|
||||||
|
# Templates are used to populate email bodies automatically.
|
||||||
|
|
||||||
|
###
|
||||||
|
new-message=new-msg
|
||||||
|
quoted-reply=reply-quoted
|
||||||
|
|
||||||
|
|
||||||
|
# The directories where the templates are stored. It takes a colon-separated
|
||||||
|
# list of directories. If this is unset or if a template cannot be found, the
|
||||||
|
# following paths will be used as a fallback in that order:
|
||||||
|
#
|
||||||
|
# ${XDG_CONFIG_HOME:-~/.config}/aerc/templates
|
||||||
|
# ${XDG_DATA_HOME:-~/.local/share}/aerc/templates
|
||||||
|
# /usr/local/share/aerc/templates
|
||||||
|
# /usr/share/aerc/templates
|
||||||
|
#
|
||||||
|
template-dirs=${XDG_CONFIG_HOME:-~/.config}/aerc/templates
|
||||||
|
|
||||||
|
# The default template to be used for new messages.
|
||||||
|
#
|
||||||
|
# default: new_message
|
||||||
|
#new-message=new_message
|
||||||
|
|
||||||
|
# The default template to be used for quoted replies.
|
||||||
|
#
|
||||||
|
# default: quoted_reply
|
||||||
|
#quoted-reply=quoted_reply
|
||||||
|
|
||||||
|
# The default template to be used for forward as body.
|
||||||
|
#
|
||||||
|
# default: forward_as_body
|
||||||
|
#forwards=forward_as_body
|
||||||
@@ -1,177 +1,115 @@
|
|||||||
# Binds are of the form <key sequence> = <command to run>
|
# Binds are of the form <key sequence> = <command to run>
|
||||||
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
|
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
|
||||||
# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
|
|
||||||
<C-p> = :prev-tab<Enter>
|
# GLOBAL Navigation & Contacts
|
||||||
<C-PgUp> = :prev-tab<Enter>
|
( = :term abook<Enter>
|
||||||
<C-n> = :next-tab<Enter>
|
,ab = :term abook<Enter>
|
||||||
<C-PgDn> = :next-tab<Enter>
|
,ca = :term khal interactive<Enter>
|
||||||
\[t = :prev-tab<Enter>
|
,c1 = :term khal interactive -a personal<Enter>
|
||||||
\]t = :next-tab<Enter>
|
,c2 = :term khal interactive -a healplaylove<Enter>
|
||||||
<C-t> = :term<Enter>
|
,c3 = :term khal interactive -a glarisegg<Enter>
|
||||||
|
,ta = :term taskwarrior-tui<Enter>
|
||||||
|
|
||||||
|
<A-p> = :prev-tab<Enter>
|
||||||
|
<A-n> = :next-tab<Enter>
|
||||||
|
<C-t> = :terminal<Enter>
|
||||||
? = :help keys<Enter>
|
? = :help keys<Enter>
|
||||||
<C-c> = :prompt 'Quit?' quit<Enter>
|
<C-c> = :prompt 'Quit?' quit<Enter>
|
||||||
<C-q> = :prompt 'Quit?' quit<Enter>
|
<C-q> = :prompt 'Quit?' quit<Enter>
|
||||||
<C-z> = :suspend<Enter>
|
<C-z> = :suspend<Enter>
|
||||||
|
|
||||||
[messages]
|
[messages]
|
||||||
# Switch between Adresses
|
# Sync
|
||||||
,1 = :change-tab phil_live<Enter>
|
u = :term /home/liph/.local/bin/mailsync<Enter>
|
||||||
,2 = :change-tab phil_notmuch<Enter>
|
|
||||||
,3 = :change-tab spam_live<Enter>
|
|
||||||
,4 = :change-tab spam_notmuch<Enter>
|
|
||||||
,5 = :change-tab proton<Enter>
|
|
||||||
|
|
||||||
# d = :modify -inbox +deleted<Enter>
|
# Navigation
|
||||||
# a = :modify -inbox<Enter>
|
j = :next<Enter>
|
||||||
# # Delete = tag as deleted and remove from inbox
|
k = :prev<Enter>
|
||||||
# d = :tag +deleted -inbox<Enter>
|
<Down> = :next<Enter>
|
||||||
|
<Up> = :prev<Enter>
|
||||||
|
g = :select 0<Enter>
|
||||||
|
G = :select -1<Enter>
|
||||||
|
J = :next-folder<Enter>
|
||||||
|
K = :prev-folder<Enter>
|
||||||
|
H = :collapse-folder<Enter>
|
||||||
|
L = :expand-folder<Enter>
|
||||||
|
<Left> = :collapse-folder<Enter>
|
||||||
|
<Right> = :expand-folder<Enter>
|
||||||
|
|
||||||
# # Archive = remove from inbox
|
# Marking/Flagging
|
||||||
# a = :tag -inbox +archive<Enter>
|
v = :mark -v<Enter>
|
||||||
# d = :move Trash<Enter>
|
V = :mark -t<Enter>
|
||||||
# a = :archive flat<Enter>
|
<Space> = :mark -t<Enter>:next<Enter>
|
||||||
|
<Esc> = :unmark -a<Enter>
|
||||||
|
|
||||||
# Expunge = actually delete (run this in Trash folder)
|
# Accounts
|
||||||
D = :prompt 'Really delete? ' 'tag +deleted'
|
,1 = :change-tab phil<Enter>
|
||||||
|
,2 = :change-tab spam<Enter>
|
||||||
|
,3 = :change-tab proton<Enter>
|
||||||
|
|
||||||
# Quick folder navigation
|
# Folders
|
||||||
,i = :cf Inbox<Enter>
|
,i = :cf Inbox<Enter>
|
||||||
,u = :cf Unread<Enter>
|
,u = :cf Unread<Enter>
|
||||||
,n = :cf Newsletters<Enter>
|
,n = :cf Newsletters<Enter>
|
||||||
,f = :cf Finance<Enter>
|
,f = :cf Finance<Enter>
|
||||||
,s = :cf Shopping<Enter>
|
,s = :cf Shopping<Enter>
|
||||||
,t = :cf Travel<Enter>
|
,v = :cf Travel<Enter>
|
||||||
,d = :cf Development<Enter>
|
,d = :cf Development<Enter>
|
||||||
,x = :cf Spam<Enter>
|
,x = :cf Spam<Enter>
|
||||||
,tr = :cf Trash<Enter>
|
,tr = :cf Trash<Enter>
|
||||||
,a = :cf All Mail<Enter>
|
,aa = :cf All Mail<Enter>
|
||||||
|
|
||||||
# o1 = :compose -H "From: Liph \<liiph@proton.me\>"<enter>
|
|
||||||
# o2 = :compose -H "From: Liph \<liiph@protonmail.com\>"<enter>
|
|
||||||
# o3 = :compose -H "From: Liph \<liiph@pm.me\>"<enter>
|
|
||||||
# o4 = :compose -H "From: Philipp Waibel \<ph.waibel@proton.me\>"<enter>
|
|
||||||
# o5 = :compose -H "From: P.Waibel \<ph.waibel@pm.me\>"<enter>
|
|
||||||
|
|
||||||
# Delete and sync
|
|
||||||
#d = :modify-labels +trash -inbox<Enter>:archive flat<Enter>
|
|
||||||
d = :modify-labels +deleted -inbox<Enter>
|
|
||||||
#d = :modify-labels +deleted -inbox<Enter>
|
|
||||||
#d = :move Trash<Enter>:exec mbsync -a && notmuch new<Enter>
|
|
||||||
# Permanently delete (Shift+D)
|
|
||||||
#D = :prompt 'Really delete?' :move Trash<Enter>
|
|
||||||
|
|
||||||
#B = :compose -H "From: waibel ph.waibel@proton.me"<Enter>
|
|
||||||
# for url handling
|
|
||||||
|
|
||||||
j = :next<Enter>
|
|
||||||
<Down> = :next<Enter>
|
|
||||||
<C-d> = :next 50%<Enter>
|
|
||||||
<C-f> = :next 100%<Enter>
|
|
||||||
<PgDn> = :next 100%<Enter>
|
|
||||||
|
|
||||||
k = :prev<Enter>
|
|
||||||
<Up> = :prev<Enter>
|
|
||||||
<C-u> = :prev 50%<Enter>
|
|
||||||
<C-b> = :prev 100%<Enter>
|
|
||||||
<PgUp> = :prev 100%<Enter>
|
|
||||||
g = :select 0<Enter>
|
|
||||||
G = :select -1<Enter>
|
|
||||||
|
|
||||||
J = :next-folder<Enter>
|
|
||||||
<C-Down> = :next-folder<Enter>
|
|
||||||
K = :prev-folder<Enter>
|
|
||||||
<C-Up> = :prev-folder<Enter>
|
|
||||||
H = :collapse-folder<Enter>
|
|
||||||
<C-Left> = :collapse-folder<Enter>
|
|
||||||
L = :expand-folder<Enter>
|
|
||||||
<C-Right> = :expand-folder<Enter>
|
|
||||||
|
|
||||||
v = :mark -t<Enter>
|
|
||||||
<Space> = :mark -t<Enter>:next<Enter>
|
|
||||||
V = :mark -v<Enter>
|
|
||||||
|
|
||||||
T = :toggle-threads<Enter>
|
|
||||||
zc = :fold<Enter>
|
|
||||||
zo = :unfold<Enter>
|
|
||||||
za = :fold -t<Enter>
|
|
||||||
zM = :fold -a<Enter>
|
|
||||||
zR = :unfold -a<Enter>
|
|
||||||
<tab> = :fold -t<Enter>
|
|
||||||
|
|
||||||
zz = :align center<Enter>
|
|
||||||
zt = :align top<Enter>
|
|
||||||
zb = :align bottom<Enter>
|
|
||||||
|
|
||||||
|
# Actions
|
||||||
<Enter> = :view<Enter>
|
<Enter> = :view<Enter>
|
||||||
#d = :choose -o y 'Really delete this message' delete-message<Enter>
|
s = :split<Enter>
|
||||||
#D = :delete<Enter>
|
S = :vsplit<Enter>
|
||||||
|
d = :modify-labels +deleted -inbox<Enter>
|
||||||
a = :archive flat<Enter>
|
a = :archive flat<Enter>
|
||||||
A = :unmark -a<Enter>:mark -T<Enter>:archive flat<Enter>
|
|
||||||
|
|
||||||
C = :compose<Enter>
|
C = :compose<Enter>
|
||||||
m = :compose<Enter>
|
m = :compose<Enter>
|
||||||
|
|
||||||
b = :bounce<space>
|
|
||||||
|
|
||||||
rr = :reply -a<Enter>
|
rr = :reply -a<Enter>
|
||||||
rq = :reply -aq<Enter>
|
rq = :reply -aq<Enter>
|
||||||
Rr = :reply<Enter>
|
Rr = :reply<Enter>
|
||||||
Rq = :reply -q<Enter>
|
Rq = :reply -q<Enter>
|
||||||
|
|
||||||
c = :cf<space>
|
|
||||||
$ = :term<space>
|
|
||||||
! = :term<space>
|
|
||||||
| = :pipe<space>
|
|
||||||
|
|
||||||
/ = :search<space>
|
/ = :search<space>
|
||||||
\ = :filter<space>
|
\ = :filter<space>
|
||||||
n = :next-result<Enter>
|
n = :next-result<Enter>
|
||||||
N = :prev-result<Enter>
|
N = :prev-result<Enter>
|
||||||
<Esc> = :clear<Enter>
|
|
||||||
|
|
||||||
s = :split<Enter>
|
# Threads
|
||||||
S = :vsplit<Enter>
|
T = :toggle-threads<Enter>
|
||||||
|
zc = :fold<Enter>
|
||||||
pl = :patch list<Enter>
|
zo = :unfold<Enter>
|
||||||
pa = :patch apply <Tab>
|
za = :fold -t<Enter>
|
||||||
pd = :patch drop <Tab>
|
|
||||||
pb = :patch rebase<Enter>
|
|
||||||
pt = :patch term<Enter>
|
|
||||||
ps = :patch switch <Tab>
|
|
||||||
|
|
||||||
[messages:folder=Drafts]
|
[messages:folder=Drafts]
|
||||||
<Enter> = :recall<Enter>
|
<Enter> = :recall<Enter>
|
||||||
|
|
||||||
[view]
|
[view]
|
||||||
|
# Navigation & UI
|
||||||
,l = :pipe urlscan<Enter>
|
|
||||||
|
|
||||||
/ = :toggle-key-passthrough<Enter>/
|
|
||||||
q = :close<Enter>
|
q = :close<Enter>
|
||||||
O = :open<Enter>
|
x = :close<Enter>
|
||||||
o = :open<Enter>
|
|
||||||
S = :save<space>
|
|
||||||
| = :pipe<space>
|
| = :pipe<space>
|
||||||
D = :delete<Enter>
|
D = :delete<Enter>
|
||||||
A = :archive flat<Enter>
|
A = :archive flat<Enter>
|
||||||
|
|
||||||
<C-l> = :open-link <space>
|
|
||||||
|
|
||||||
f = :forward<Enter>
|
f = :forward<Enter>
|
||||||
rr = :reply -a<Enter>
|
rr = :reply -a<Enter>
|
||||||
rq = :reply -aq<Enter>
|
rq = :reply -aq<Enter>
|
||||||
Rr = :reply<Enter>
|
Rr = :reply<Enter>
|
||||||
Rq = :reply -q<Enter>
|
Rq = :reply -q<Enter>
|
||||||
|
|
||||||
H = :toggle-headers<Enter>
|
H = :toggle-headers<Enter>
|
||||||
<C-k> = :prev-part<Enter>
|
,l = :pipe urlscan<Enter>
|
||||||
<C-Up> = :prev-part<Enter>
|
|
||||||
<C-j> = :next-part<Enter>
|
# Attachment Handling
|
||||||
<C-Down> = :next-part<Enter>
|
n = :next-part<Enter>
|
||||||
|
p = :prev-part<Enter>
|
||||||
J = :next<Enter>
|
J = :next<Enter>
|
||||||
<C-Right> = :next<Enter>
|
|
||||||
K = :prev<Enter>
|
K = :prev<Enter>
|
||||||
<C-Left> = :prev<Enter>
|
o = :open<Enter>
|
||||||
|
O = :open<Enter>
|
||||||
|
s = :save ~/Downloads/<Enter>
|
||||||
|
S = :save<space>
|
||||||
|
,sa = :save -a ~/Downloads/<Enter>
|
||||||
|
|
||||||
[view::passthrough]
|
[view::passthrough]
|
||||||
$noinherit = true
|
$noinherit = true
|
||||||
@@ -179,65 +117,45 @@ $ex = <C-x>
|
|||||||
<Esc> = :toggle-key-passthrough<Enter>
|
<Esc> = :toggle-key-passthrough<Enter>
|
||||||
|
|
||||||
[compose]
|
[compose]
|
||||||
# Keybindings used when the embedded terminal is not selected in the compose
|
# General compose view
|
||||||
# view
|
|
||||||
$noinherit = true
|
$noinherit = true
|
||||||
$ex = <C-x>
|
$ex = <C-x>
|
||||||
$complete = <C-o>
|
$complete = <C-o>
|
||||||
<C-k> = :prev-field<Enter>
|
<C-k> = :attach<space>
|
||||||
<C-Up> = :prev-field<Enter>
|
<C-a> = :attach<space>
|
||||||
<C-j> = :next-field<Enter>
|
<F4> = :attach<space>
|
||||||
<C-Down> = :next-field<Enter>
|
<A-p> = :prev-tab<Enter>
|
||||||
<A-p> = :switch-account -p<Enter>
|
<A-n> = :next-tab<Enter>
|
||||||
<C-Left> = :switch-account -p<Enter>
|
|
||||||
<A-n> = :switch-account -n<Enter>
|
|
||||||
<C-Right> = :switch-account -n<Enter>
|
|
||||||
<tab> = :next-field<Enter>
|
<tab> = :next-field<Enter>
|
||||||
<backtab> = :prev-field<Enter>
|
<backtab> = :prev-field<Enter>
|
||||||
<C-p> = :prev-tab<Enter>
|
|
||||||
<C-PgUp> = :prev-tab<Enter>
|
|
||||||
<C-n> = :next-tab<Enter>
|
|
||||||
<C-PgDn> = :next-tab<Enter>
|
|
||||||
|
|
||||||
[compose::editor]
|
[compose::editor]
|
||||||
# Keybindings used when the embedded terminal is selected in the compose view
|
# While focused in the text editor (nvim)
|
||||||
$noinherit = true
|
$noinherit = true
|
||||||
$ex = <C-x>
|
$ex = <C-x>
|
||||||
<C-k> = :prev-field<Enter>
|
<C-k> = :attach<space>
|
||||||
<C-Up> = :prev-field<Enter>
|
<C-a> = :attach<space>
|
||||||
<C-j> = :next-field<Enter>
|
<F4> = :attach<space>
|
||||||
<C-Down> = :next-field<Enter>
|
|
||||||
<C-p> = :prev-tab<Enter>
|
|
||||||
<C-PgUp> = :prev-tab<Enter>
|
|
||||||
<C-n> = :next-tab<Enter>
|
|
||||||
<C-PgDn> = :next-tab<Enter>
|
|
||||||
|
|
||||||
|
|
||||||
# Insert signatures with Ctrl+s + number
|
|
||||||
<C-s>1 = :exec cat /home/liph/.config/aerc/sigs/formal.txt<Enter>
|
|
||||||
<C-s>2 = :read ~/.config/aerc/sigs/formal_eng.txt<Enter>
|
|
||||||
<C-s>3 = :read ~/.config/aerc/sigs/liph.txt<Enter>
|
|
||||||
<C-s>4 = :read ~/.config/aerc/sigs/phil.txt<Enter>
|
|
||||||
|
|
||||||
[compose::review]
|
[compose::review]
|
||||||
# Keybindings used when reviewing a message to be sent
|
# After exiting the editor
|
||||||
# Inline comments are used as descriptions on the review screen
|
y = :multipart text/html<Enter>:send<Enter>
|
||||||
y = :send<Enter> # Send
|
n = :abort<Enter>
|
||||||
n = :abort<Enter> # Abort (discard message, no confirmation)
|
v = :preview<Enter>
|
||||||
s = :sign<Enter> # Toggle signing
|
p = :postpone<Enter>
|
||||||
x = :encrypt<Enter> # Toggle encryption to all recipients
|
q = :choose -o d discard abort -o p postpone postpone<Enter>
|
||||||
v = :preview<Enter> # Preview message
|
e = :edit<Enter>
|
||||||
p = :postpone<Enter> # Postpone
|
a = :attach<space>
|
||||||
q = :choose -o d discard abort -o p postpone postpone<Enter> # Abort or postpone
|
<C-k> = :attach<space>
|
||||||
e = :edit<Enter> # Edit (body and headers)
|
<C-a> = :attach<space>
|
||||||
a = :attach<space> # Add attachment
|
d = :detach<space>
|
||||||
d = :detach<space> # Remove attachment
|
|
||||||
|
|
||||||
[terminal]
|
[terminal]
|
||||||
$noinherit = true
|
# Clear keys to allow passthrough to Khal/Taskwarrior
|
||||||
$ex = <C-x>
|
$ex = <C-x>
|
||||||
|
<C-p> =
|
||||||
<C-p> = :prev-tab<Enter>
|
<C-n> =
|
||||||
<C-n> = :next-tab<Enter>
|
<tab> =
|
||||||
<C-PgUp> = :prev-tab<Enter>
|
<backtab> =
|
||||||
<C-PgDn> = :next-tab<Enter>
|
<A-p> = :prev-tab<Enter>
|
||||||
|
<A-n> = :next-tab<Enter>
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# Binds are of the form <key sequence> = <command to run>
|
||||||
|
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
|
||||||
|
|
||||||
|
# GLOBAL Navigation & Contacts
|
||||||
|
( = :term abook<Enter>
|
||||||
|
,ab = :term abook<Enter>
|
||||||
|
,ca = :term khal interactive<Enter>
|
||||||
|
<C-p> = :prev-tab<Enter>
|
||||||
|
<C-n> = :next-tab<Enter>
|
||||||
|
<C-t> = :terminal<Enter>
|
||||||
|
? = :help keys<Enter>
|
||||||
|
<C-c> = :prompt 'Quit?' quit<Enter>
|
||||||
|
<C-q> = :prompt 'Quit?' quit<Enter>
|
||||||
|
<C-z> = :suspend<Enter>
|
||||||
|
|
||||||
|
[messages]
|
||||||
|
# Sync
|
||||||
|
u = :term /home/liph/.local/bin/mailsync<Enter>
|
||||||
|
|
||||||
|
# Navigation
|
||||||
|
j = :next<Enter>
|
||||||
|
k = :prev<Enter>
|
||||||
|
<Down> = :next<Enter>
|
||||||
|
<Up> = :prev<Enter>
|
||||||
|
g = :select 0<Enter>
|
||||||
|
G = :select -1<Enter>
|
||||||
|
J = :next-folder<Enter>
|
||||||
|
K = :prev-folder<Enter>
|
||||||
|
H = :collapse-folder<Enter>
|
||||||
|
L = :expand-folder<Enter>
|
||||||
|
<Left> = :collapse-folder<Enter>
|
||||||
|
<Right> = :expand-folder<Enter>
|
||||||
|
|
||||||
|
# Accounts
|
||||||
|
,1 = :change-tab phil<Enter>
|
||||||
|
,2 = :change-tab spam<Enter>
|
||||||
|
,3 = :change-tab proton<Enter>
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
,i = :cf Inbox<Enter>
|
||||||
|
,u = :cf Unread<Enter>
|
||||||
|
,n = :cf Newsletters<Enter>
|
||||||
|
,f = :cf Finance<Enter>
|
||||||
|
,s = :cf Shopping<Enter>
|
||||||
|
,t = :cf Travel<Enter>
|
||||||
|
,d = :cf Development<Enter>
|
||||||
|
,x = :cf Spam<Enter>
|
||||||
|
,tr = :cf Trash<Enter>
|
||||||
|
,am = :cf All Mail<Enter>
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
<Enter> = :view<Enter>
|
||||||
|
s = :split<Enter>
|
||||||
|
S = :vsplit<Enter>
|
||||||
|
d = :modify-labels +deleted -inbox<Enter>
|
||||||
|
a = :archive flat<Enter>
|
||||||
|
C = :compose<Enter>
|
||||||
|
m = :compose<Enter>
|
||||||
|
rr = :reply -a<Enter>
|
||||||
|
rq = :reply -aq<Enter>
|
||||||
|
Rr = :reply<Enter>
|
||||||
|
Rq = :reply -q<Enter>
|
||||||
|
/ = :search<space>
|
||||||
|
\ = :filter<space>
|
||||||
|
n = :next-result<Enter>
|
||||||
|
N = :prev-result<Enter>
|
||||||
|
|
||||||
|
# Threads
|
||||||
|
T = :toggle-threads<Enter>
|
||||||
|
zc = :fold<Enter>
|
||||||
|
zo = :unfold<Enter>
|
||||||
|
za = :fold -t<Enter>
|
||||||
|
|
||||||
|
[messages:folder=Drafts]
|
||||||
|
<Enter> = :recall<Enter>
|
||||||
|
|
||||||
|
[view]
|
||||||
|
# Navigation & UI
|
||||||
|
q = :close<Enter>
|
||||||
|
x = :close<Enter>
|
||||||
|
| = :pipe<space>
|
||||||
|
D = :delete<Enter>
|
||||||
|
A = :archive flat<Enter>
|
||||||
|
f = :forward<Enter>
|
||||||
|
rr = :reply -a<Enter>
|
||||||
|
rq = :reply -aq<Enter>
|
||||||
|
Rr = :reply<Enter>
|
||||||
|
Rq = :reply -q<Enter>
|
||||||
|
H = :toggle-headers<Enter>
|
||||||
|
,l = :pipe urlscan<Enter>
|
||||||
|
|
||||||
|
# Attachment Handling
|
||||||
|
n = :next-part<Enter>
|
||||||
|
p = :prev-part<Enter>
|
||||||
|
J = :next<Enter>
|
||||||
|
K = :prev<Enter>
|
||||||
|
o = :open<Enter>
|
||||||
|
O = :open<Enter>
|
||||||
|
s = :pipe -m /home/liph/.local/bin/save-attachment<Enter>
|
||||||
|
S = :save<space>
|
||||||
|
,sa = :save -a<Enter>
|
||||||
|
|
||||||
|
[view::passthrough]
|
||||||
|
$noinherit = true
|
||||||
|
$ex = <C-x>
|
||||||
|
<Esc> = :toggle-key-passthrough<Enter>
|
||||||
|
|
||||||
|
[compose]
|
||||||
|
# General compose view
|
||||||
|
$noinherit = true
|
||||||
|
$ex = <C-x>
|
||||||
|
$complete = <C-o>
|
||||||
|
<C-k> = :attach<space>
|
||||||
|
<C-a> = :attach<space>
|
||||||
|
<F4> = :attach<space>
|
||||||
|
<C-p> = :prev-tab<Enter>
|
||||||
|
<C-n> = :next-tab<Enter>
|
||||||
|
<tab> = :next-field<Enter>
|
||||||
|
<backtab> = :prev-field<Enter>
|
||||||
|
|
||||||
|
[compose::editor]
|
||||||
|
# While focused in the text editor (nvim)
|
||||||
|
$noinherit = true
|
||||||
|
$ex = <C-x>
|
||||||
|
<C-k> = :attach<space>
|
||||||
|
<C-a> = :attach<space>
|
||||||
|
<F4> = :attach<space>
|
||||||
|
|
||||||
|
[compose::review]
|
||||||
|
# After exiting the editor
|
||||||
|
y = :multipart text/html<Enter>:send<Enter>
|
||||||
|
n = :abort<Enter>
|
||||||
|
v = :preview<Enter>
|
||||||
|
p = :postpone<Enter>
|
||||||
|
q = :choose -o d discard abort -o p postpone postpone<Enter>
|
||||||
|
e = :edit<Enter>
|
||||||
|
a = :attach<space>
|
||||||
|
<C-k> = :attach<space>
|
||||||
|
<C-a> = :attach<space>
|
||||||
|
d = :detach<space>
|
||||||
|
|
||||||
|
[terminal]
|
||||||
|
$noinherit = true
|
||||||
|
$ex = <C-x>
|
||||||
|
<C-p> = :prev-tab<Enter>
|
||||||
|
<C-n> = :next-tab<Enter>
|
||||||
@@ -1,42 +1,37 @@
|
|||||||
# Priority Management
|
# Priority Management
|
||||||
Priority 1=tag:priority1 and tag:inbox
|
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
Priority 2=tag:priority2 and tag:inbox
|
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
All Priority=tag:priority1 or tag:priority2
|
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
# Main Views
|
# Main Views
|
||||||
Inbox=tag:inbox
|
Inbox=tag:inbox and not tag:deleted and not tag:spam and not (from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so)
|
||||||
Unread=tag:unread
|
Unread=tag:unread and not tag:deleted and not tag:spam
|
||||||
Starred=tag:flagged or tag:starred
|
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
|
||||||
Archive=not tag:inbox and not tag:spam and not tag:deleted
|
Archive=not tag:inbox and not tag:spam and not tag:deleted
|
||||||
Sent=folder:Sent or tag:sent
|
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
|
||||||
Drafts=folder:Drafts or tag:draft
|
Mail/Drafts=folder:Drafts or tag:draft
|
||||||
Spam=tag:spam or folder:Spam
|
Mail/Spam=tag:spam and not tag:deleted
|
||||||
Trash=tag:deleted or folder:Trash
|
Mail/Trash=tag:deleted
|
||||||
|
|
||||||
# Categories
|
# Categories
|
||||||
# Work=tag:work and tag:inbox
|
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
|
||||||
# Personal=tag:personal and tag:inbox
|
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
|
||||||
Finance=tag:finance and tag:inbox
|
|
||||||
# Social=tag:social and tag:inbox
|
|
||||||
# Mailing Lists=tag:mailinglist and tag:inbox
|
|
||||||
Newsletters=tag:newsletter and tag:inbox
|
|
||||||
# Automated=tag:automated
|
|
||||||
|
|
||||||
# Time-based
|
# Time-based
|
||||||
Today=date:today and tag:inbox
|
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
|
||||||
This Week=date:week and tag:inbox
|
Time/This Week=date:6days..today and tag:inbox and not tag:deleted and not tag:spam
|
||||||
This Month=date:month and tag:inbox
|
Time/This Month=date:month and tag:inbox and not tag:deleted and not tag:spam
|
||||||
Last 7 Days=date:7days..today
|
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
# Special Queries
|
# Special Queries
|
||||||
Needs Reply=tag:inbox and not tag:replied
|
Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
|
||||||
Attachments=tag:attachment and tag:inbox
|
Attachments=tag:attachment and tag:inbox and not tag:deleted and not tag:spam
|
||||||
Important=tag:flagged or tag:priority1
|
Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
|
||||||
Action Required=tag:todo or tag:followup
|
Action Required=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
# Unprocessed
|
# Unprocessed
|
||||||
New Mail=tag:new
|
New Mail=tag:new and not tag:deleted and not tag:spam
|
||||||
Untagged=tag:inbox and not tag:priority1 and not tag:priority2 and not tag:work and not tag:personal and not tag:finance
|
Untagged=tag:inbox and not tag:priority1 and not tag:priority2 and not tag:work and not tag:personal and not tag:finance and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
# All Mail
|
# All Mail
|
||||||
Everything=*
|
Everything=*
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Standard Main Views
|
||||||
|
Inbox=tag:inbox and not tag:deleted and not tag:spam and not (from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so)
|
||||||
|
Unread=tag:unread and not tag:deleted and not tag:spam
|
||||||
|
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
|
||||||
|
Archive=not tag:inbox and not tag:spam and not tag:deleted
|
||||||
|
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
|
||||||
|
Mail/Drafts=folder:Drafts or tag:draft
|
||||||
|
Mail/Spam=tag:spam and not tag:deleted
|
||||||
|
Mail/Trash=tag:deleted
|
||||||
|
|
||||||
|
# Community Folder Stack
|
||||||
|
Community=(from:@makesomebreathingspace.com or from:@healplaylove.ch or from:@circle.so) and date:today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Community - Breathing Space
|
||||||
|
Community/Breathing Space=from:@makesomebreathingspace.com and not tag:deleted and not tag:spam
|
||||||
|
Community/Breathing Space/Today=from:@makesomebreathingspace.com and date:today and not tag:deleted and not tag:spam
|
||||||
|
Community/Breathing Space/This Week=from:@makesomebreathingspace.com and date:6days..today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Community - Heal Play Love
|
||||||
|
Community/Heal Play Love=(from:@healplaylove.ch or from:@circle.so) and not tag:deleted and not tag:spam
|
||||||
|
Community/Heal Play Love/Today=(from:@healplaylove.ch or from:@circle.so) and date:today and not tag:deleted and not tag:spam
|
||||||
|
Community/Heal Play Love/This Week=(from:@healplaylove.ch or from:@circle.so) and date:6days..today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Categories
|
||||||
|
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Priority Management
|
||||||
|
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Time-based
|
||||||
|
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Time/This Week=date:week and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Status/Action
|
||||||
|
Action/Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
|
||||||
|
Action/Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
|
||||||
|
Action/Todo=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# All Mail
|
||||||
|
Everything=*
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Standard Main Views
|
||||||
|
Inbox=tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Unread=tag:unread and not tag:deleted and not tag:spam
|
||||||
|
Starred=(tag:flagged or tag:starred) and not tag:deleted and not tag:spam
|
||||||
|
Archive=not tag:inbox and not tag:spam and not tag:deleted
|
||||||
|
Mail/Sent=(folder:Sent or tag:sent) and not tag:deleted
|
||||||
|
Mail/Drafts=folder:Drafts or tag:draft
|
||||||
|
Mail/Spam=tag:spam and not tag:deleted
|
||||||
|
Mail/Trash=tag:deleted
|
||||||
|
|
||||||
|
# Community Folder Stack
|
||||||
|
Community/Breathing Space=from:@makesomebreathingspace.com and not tag:deleted and not tag:spam
|
||||||
|
Community/Heal Play Love=(from:@healplaylove.ch or from:@circle.so) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Categories
|
||||||
|
Finance=tag:finance and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Newsletters=tag:newsletter and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Priority Management
|
||||||
|
Priority/1=tag:priority1 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Priority/2=tag:priority2 and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Priority/All=(tag:priority1 or tag:priority2) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Time-based
|
||||||
|
Time/Today=date:today and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Time/This Week=date:week and tag:inbox and not tag:deleted and not tag:spam
|
||||||
|
Time/Last 7 Days=date:7days..today and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# Status/Action
|
||||||
|
Action/Needs Reply=tag:inbox and not tag:replied and not tag:deleted and not tag:spam
|
||||||
|
Action/Important=(tag:flagged or tag:priority1) and not tag:deleted and not tag:spam
|
||||||
|
Action/Todo=(tag:todo or tag:followup) and not tag:deleted and not tag:spam
|
||||||
|
|
||||||
|
# All Mail
|
||||||
|
Everything=*
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
*.default=true
|
|
||||||
*.normal=true
|
|
||||||
|
|
||||||
default.fg=#cdd6f4
|
|
||||||
|
|
||||||
error.fg=#f38ba8
|
|
||||||
warning.fg=#fab387
|
|
||||||
success.fg=#a6e3a1
|
|
||||||
|
|
||||||
tab.fg=#6c7086
|
|
||||||
tab.bg=#181825
|
|
||||||
tab.selected.fg=#cdd6f4
|
|
||||||
tab.selected.bg=#1e1e2e
|
|
||||||
tab.selected.bold=true
|
|
||||||
|
|
||||||
border.fg=#11111b
|
|
||||||
border.bold=true
|
|
||||||
|
|
||||||
msglist_unread.bold=true
|
|
||||||
msglist_flagged.fg=#f9e2af
|
|
||||||
msglist_flagged.bold=true
|
|
||||||
msglist_result.fg=#89b4fa
|
|
||||||
msglist_result.bold=true
|
|
||||||
msglist_*.selected.bold=true
|
|
||||||
msglist_*.selected.bg=#313244
|
|
||||||
|
|
||||||
dirlist_*.selected.bold=true
|
|
||||||
dirlist_*.selected.bg=#313244
|
|
||||||
|
|
||||||
statusline_default.fg=#9399b2
|
|
||||||
statusline_default.bg=#313244
|
|
||||||
statusline_error.bold=true
|
|
||||||
statusline_success.bold=true
|
|
||||||
|
|
||||||
selector_focused.bg=#313244
|
|
||||||
|
|
||||||
completion_default.selected.bg=#313244
|
|
||||||
|
|
||||||
[viewer]
|
|
||||||
url.fg=#89b4fa
|
|
||||||
url.underline=true
|
|
||||||
header.bold=true
|
|
||||||
signature.dim=true
|
|
||||||
diff_meta.bold=true
|
|
||||||
diff_chunk.fg=#89b4fa
|
|
||||||
diff_chunk_func.fg=#89b4fa
|
|
||||||
diff_chunk_func.bold=true
|
|
||||||
diff_add.fg=#a6e3a1
|
|
||||||
diff_del.fg=#f38ba8
|
|
||||||
quote_*.fg=#6c7086
|
|
||||||
quote_1.fg=#9399b2
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
*.default=true
|
||||||
|
*.normal=true
|
||||||
|
*.selected.bold=true
|
||||||
|
*.selected.bg=#ebdbb2
|
||||||
|
*.selected.fg=#282828
|
||||||
|
|
||||||
|
default.fg=#89b482
|
||||||
|
|
||||||
|
error.fg=#cc241d
|
||||||
|
warning.fg=#d65d0e
|
||||||
|
success.fg=#D8A657
|
||||||
|
|
||||||
|
tab.fg=#7c6f64
|
||||||
|
tab.bg=#282823
|
||||||
|
tab.selected.fg=#d5c4a1
|
||||||
|
tab.selected.bg=#282828
|
||||||
|
tab.selected.bold=true
|
||||||
|
|
||||||
|
border.fg=#d5c4a1
|
||||||
|
border.bold=true
|
||||||
|
|
||||||
|
msglist_unread.bold=true
|
||||||
|
msglist_unread.fg=#d8a657
|
||||||
|
msglist_unread.selected.bg=#ebdbb2
|
||||||
|
msglist_unread.selected.fg=#282828
|
||||||
|
msglist_flagged.fg=#f9e2af
|
||||||
|
msglist_flagged.bold=true
|
||||||
|
msglist_result.fg=#458588
|
||||||
|
msglist_result.bold=true
|
||||||
|
msglist_marked.fg=#d3869b
|
||||||
|
msglist_marked.bold=true
|
||||||
|
msglist_*.selected.bold=true
|
||||||
|
msglist_*.selected.bg=#ebdbb2
|
||||||
|
|
||||||
|
dirlist_*.selected.bold=true
|
||||||
|
dirlist_*.selected.bg=#ebdbb2
|
||||||
|
|
||||||
|
statusline_default.fg=#ebdbb2
|
||||||
|
statusline_default.bg=#282828
|
||||||
|
statusline_error.bold=true
|
||||||
|
statusline_success.bold=true
|
||||||
|
|
||||||
|
[user]
|
||||||
|
folder_sub.fg=#fabd2f
|
||||||
|
folder_sub.bold=true
|
||||||
|
|
||||||
|
[viewer]
|
||||||
|
url.fg=#458588
|
||||||
|
url.underline=true
|
||||||
|
header.bold=true
|
||||||
|
signature.dim=true
|
||||||
|
diff_meta.bold=true
|
||||||
|
diff_chunk.fg=#458588
|
||||||
|
diff_chunk_func.fg=#458588
|
||||||
|
diff_chunk_func.bold=true
|
||||||
|
diff_add.fg=#D8A657
|
||||||
|
diff_del.fg=#cc241d
|
||||||
|
quote_*.fg=#d5c4a1
|
||||||
|
quote_1.fg=#b16286
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# vim: filetype=dosini
|
||||||
|
|
||||||
|
# Rosé Pine Theme for Aerc
|
||||||
|
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
|
||||||
|
# Save as ~/.config/aerc/stylesets/rose-pine
|
||||||
|
|
||||||
|
# Base styling
|
||||||
|
default.fg=#9ccfd8
|
||||||
|
default.bg=#191724
|
||||||
|
|
||||||
|
success.fg=#31748f
|
||||||
|
error.fg=#eb6f92
|
||||||
|
warning.fg=#f6c177
|
||||||
|
|
||||||
|
title.bg=#26233a
|
||||||
|
title.fg=#c4a7e7
|
||||||
|
|
||||||
|
header.fg=#c4a7e7
|
||||||
|
|
||||||
|
spinner.fg=#6e6a86
|
||||||
|
|
||||||
|
part_filename.fg=#f6c177
|
||||||
|
part_mimetype.fg=#c4a7e7
|
||||||
|
|
||||||
|
# Global selection
|
||||||
|
*.selected.fg=#191724
|
||||||
|
*.selected.bg=#ebbcba
|
||||||
|
*.selected.bold=true
|
||||||
|
|
||||||
|
# Message list
|
||||||
|
msglist_default.fg=#9ccfd8
|
||||||
|
msglist_default.bg=#1f1d2e
|
||||||
|
msglist_unread.fg=#524f67
|
||||||
|
msglist_unread.bold=true
|
||||||
|
msglist_read.fg=#6e6a86
|
||||||
|
msglist_flagged.fg=#f6c177
|
||||||
|
msglist_flagged.bold=true
|
||||||
|
msglist_deleted.bg=#eb6f92
|
||||||
|
msglist_deleted.fg=#191724
|
||||||
|
msglist_deleted.bold=true
|
||||||
|
msglist_marked.fg=#191724
|
||||||
|
msglist_marked.bg=#31748f
|
||||||
|
msglist_marked.bold=true
|
||||||
|
|
||||||
|
# Status bar
|
||||||
|
statusline_*.bold=true
|
||||||
|
statusline_*.fg=#191724
|
||||||
|
statusline_default.fg=#9ccfd8
|
||||||
|
statusline_default.bg=#191724
|
||||||
|
statusline_error.bg=#eb6f92
|
||||||
|
statusline_success.bg=#31748f
|
||||||
|
|
||||||
|
# Completion
|
||||||
|
completion_default.fg=#9ccfd8
|
||||||
|
completion_default.bg=#1f1d2e
|
||||||
|
completion_gutter.bg=#26233a
|
||||||
|
completion_description.bg=#26233a
|
||||||
|
|
||||||
|
# Scrollbars
|
||||||
|
*_pill.fg=#191724
|
||||||
|
*_pill.bg=#6e6a86
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border.fg=#191724
|
||||||
|
border.bg=#191724
|
||||||
|
|
||||||
|
# Tab
|
||||||
|
tab.bg=#191724
|
||||||
|
tab.selected.fg=#ebbcba
|
||||||
|
tab.selected.bg=#191724
|
||||||
|
tab.selected.bold=true
|
||||||
|
|
||||||
|
[viewer]
|
||||||
|
url.fg=#c4a7e7
|
||||||
|
url.underline=true
|
||||||
|
header.bold=true
|
||||||
|
signature.dim=true
|
||||||
|
diff_meta.bold=true
|
||||||
|
diff_chunk.fg=#c4a7e7
|
||||||
|
diff_chunk_func.fg=#c4a7e7
|
||||||
|
diff_chunk_func.bold=true
|
||||||
|
diff_add.fg=#524f67
|
||||||
|
diff_del.fg=#eb6f92
|
||||||
|
quote_*.fg=#908caa
|
||||||
|
quote_1.fg=#21202e
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# vim: filetype=dosini
|
||||||
|
|
||||||
|
# Rosé Pine Theme for Aerc
|
||||||
|
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
|
||||||
|
# Save as ~/.config/aerc/stylesets/rose-pine
|
||||||
|
|
||||||
|
# Base styling
|
||||||
|
default.fg=#9ccfd8
|
||||||
|
default.bg=#191724
|
||||||
|
|
||||||
|
success.fg=#31748f
|
||||||
|
error.fg=#eb6f92
|
||||||
|
warning.fg=#f6c177
|
||||||
|
|
||||||
|
title.bg=#26233a
|
||||||
|
title.fg=#c4a7e7
|
||||||
|
|
||||||
|
header.fg=#c4a7e7
|
||||||
|
|
||||||
|
spinner.fg=#6e6a86
|
||||||
|
|
||||||
|
part_filename.fg=#f6c177
|
||||||
|
part_mimetype.fg=#c4a7e7
|
||||||
|
|
||||||
|
# Global selection
|
||||||
|
*.selected.fg=#191724
|
||||||
|
*.selected.bg=#ebbcba
|
||||||
|
*.selected.bold=true
|
||||||
|
|
||||||
|
# Message list
|
||||||
|
msglist_default.fg=#9ccfd8
|
||||||
|
msglist_default.bg=#1f1d2e
|
||||||
|
msglist_unread.fg=#524f67
|
||||||
|
msglist_unread.bold=true
|
||||||
|
msglist_read.fg=#6e6a86
|
||||||
|
msglist_flagged.fg=#f6c177
|
||||||
|
msglist_flagged.bold=true
|
||||||
|
msglist_deleted.bg=#eb6f92
|
||||||
|
msglist_deleted.fg=#191724
|
||||||
|
msglist_deleted.bold=true
|
||||||
|
msglist_marked.fg=#191724
|
||||||
|
msglist_marked.bg=#31748f
|
||||||
|
msglist_marked.bold=true
|
||||||
|
|
||||||
|
# Status bar
|
||||||
|
statusline_*.bold=true
|
||||||
|
statusline_*.fg=#191724
|
||||||
|
statusline_default.fg=#9ccfd8
|
||||||
|
statusline_default.bg=#191724
|
||||||
|
statusline_error.bg=#eb6f92
|
||||||
|
statusline_success.bg=#31748f
|
||||||
|
|
||||||
|
# Completion
|
||||||
|
completion_default.fg=#9ccfd8
|
||||||
|
completion_default.bg=#1f1d2e
|
||||||
|
completion_gutter.bg=#26233a
|
||||||
|
completion_description.bg=#26233a
|
||||||
|
|
||||||
|
# Scrollbars
|
||||||
|
*_pill.fg=#191724
|
||||||
|
*_pill.bg=#6e6a86
|
||||||
|
|
||||||
|
# Border
|
||||||
|
border.fg=#191724
|
||||||
|
border.bg=#191724
|
||||||
|
|
||||||
|
# Tab
|
||||||
|
tab.bg=#191724
|
||||||
|
tab.selected.fg=#ebbcba
|
||||||
|
tab.selected.bg=#191724
|
||||||
|
tab.selected.bold=true
|
||||||
|
|
||||||
|
[viewer]
|
||||||
|
url.fg=#c4a7e7
|
||||||
|
url.underline=true
|
||||||
|
header.bold=true
|
||||||
|
signature.dim=true
|
||||||
|
diff_meta.bold=true
|
||||||
|
diff_chunk.fg=#c4a7e7
|
||||||
|
diff_chunk_func.fg=#c4a7e7
|
||||||
|
diff_chunk_func.bold=true
|
||||||
|
diff_add.fg=#524f67
|
||||||
|
diff_del.fg=#eb6f92
|
||||||
|
quote_*.fg=#908caa
|
||||||
|
quote_1.fg=#21202e
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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.**
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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! 🎉**
|
||||||
@@ -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**
|
||||||
@@ -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! 🚀**
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
DB_PASSWORD=
|
||||||
|
DB_USERNAME=
|
||||||
|
DB_DATABASE_NAME=
|
||||||
|
DB_HOST=
|
||||||
|
|
||||||
|
PUID=33
|
||||||
|
PGID=1000
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 %}
|
||||||
@@ -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/
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 %}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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! 🎉"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"binaries":["atuin"],"binary_aliases":{},"cdylibs":[],"cstaticlibs":[],"install_layout":"flat","install_prefix":"/home/liph/.atuin/bin","modify_path":true,"provider":{"source":"cargo-dist","version":"0.31.0"},"source":{"app_name":"atuin","name":"atuin","owner":"atuinsh","release_type":"github"},"version":"18.13.3"}
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
## Base directory for Atuin data files (databases, keys, session, etc.)
|
||||||
|
## All data file paths default to being relative to this directory.
|
||||||
|
## linux/mac: ~/.local/share/atuin (or XDG_DATA_HOME/atuin)
|
||||||
|
## windows: %USERPROFILE%/.local/share/atuin
|
||||||
|
# data_dir = "~/.local/share/atuin"
|
||||||
|
|
||||||
|
## where to store your database, default is your system data directory
|
||||||
|
## linux/mac: ~/.local/share/atuin/history.db
|
||||||
|
## windows: %USERPROFILE%/.local/share/atuin/history.db
|
||||||
|
# db_path = "~/.history.db"
|
||||||
|
|
||||||
|
## where to store your encryption key, default is your system data directory
|
||||||
|
## linux/mac: ~/.local/share/atuin/key
|
||||||
|
## windows: %USERPROFILE%/.local/share/atuin/key
|
||||||
|
# key_path = "~/.key"
|
||||||
|
|
||||||
|
## where to store your auth session token, default is your system data directory
|
||||||
|
## linux/mac: ~/.local/share/atuin/session
|
||||||
|
## windows: %USERPROFILE%/.local/share/atuin/session
|
||||||
|
# session_path = "~/.session"
|
||||||
|
|
||||||
|
## date format used, either "us" or "uk"
|
||||||
|
# dialect = "us"
|
||||||
|
|
||||||
|
## default timezone to use when displaying time
|
||||||
|
## either "l", "local" to use the system's current local timezone, or an offset
|
||||||
|
## from UTC in the format of "<+|->H[H][:M[M][:S[S]]]"
|
||||||
|
## for example: "+9", "-05", "+03:30", "-01:23:45", etc.
|
||||||
|
# timezone = "local"
|
||||||
|
|
||||||
|
## enable or disable automatic sync
|
||||||
|
# auto_sync = true
|
||||||
|
|
||||||
|
## enable or disable automatic update checks
|
||||||
|
# update_check = true
|
||||||
|
|
||||||
|
## address of the sync server
|
||||||
|
# sync_address = "https://api.atuin.sh"
|
||||||
|
|
||||||
|
## how often to sync history. note that this is only triggered when a command
|
||||||
|
## is ran, so sync intervals may well be longer
|
||||||
|
## set it to 0 to sync after every command
|
||||||
|
# sync_frequency = "10m"
|
||||||
|
|
||||||
|
## which search mode to use
|
||||||
|
## possible values: prefix, fulltext, fuzzy, skim
|
||||||
|
# search_mode = "daemon-fuzzy"
|
||||||
|
|
||||||
|
## which filter mode to use by default
|
||||||
|
## possible values: "global", "host", "session", "session-preload", "directory", "workspace"
|
||||||
|
## consider using search.filters to customize the enablement and order of filter modes
|
||||||
|
filter_mode = "host"
|
||||||
|
|
||||||
|
## With workspace filtering enabled, Atuin will filter for commands executed
|
||||||
|
## in any directory within a git repository tree (default: false).
|
||||||
|
##
|
||||||
|
## To use workspace mode by default when available, set this to true and
|
||||||
|
## set filter_mode to "workspace" or leave it unspecified and
|
||||||
|
## set search.filters to include "workspace" before other filter modes.
|
||||||
|
# workspaces = false
|
||||||
|
|
||||||
|
## which filter mode to use when atuin is invoked from a shell up-key binding
|
||||||
|
## the accepted values are identical to those of "filter_mode"
|
||||||
|
## leave unspecified to use same mode set in "filter_mode"
|
||||||
|
filter_mode_shell_up_key_binding = "host"
|
||||||
|
|
||||||
|
## which search mode to use when atuin is invoked from a shell up-key binding
|
||||||
|
## the accepted values are identical to those of "search_mode"
|
||||||
|
## leave unspecified to use same mode set in "search_mode"
|
||||||
|
# search_mode_shell_up_key_binding = "fuzzy"
|
||||||
|
|
||||||
|
## which style to use
|
||||||
|
## possible values: auto, full, compact
|
||||||
|
# style = "auto"
|
||||||
|
|
||||||
|
## the maximum number of lines the interface should take up
|
||||||
|
## set it to 0 to always go full screen
|
||||||
|
inline_height = 30
|
||||||
|
|
||||||
|
## the maximum number of lines the interface should take up
|
||||||
|
## when atuin is invoked from a shell up-key binding
|
||||||
|
## the accepted values are identical to those of "inline_height"
|
||||||
|
# inline_height_shell_up_key_binding = 0
|
||||||
|
|
||||||
|
## Invert the UI - put the search bar at the top , Default to `false`
|
||||||
|
# invert = false
|
||||||
|
|
||||||
|
## enable or disable showing a preview of the selected command
|
||||||
|
## useful when the command is longer than the terminal width and is cut off
|
||||||
|
show_preview = true
|
||||||
|
|
||||||
|
## what to do when the escape key is pressed when searching
|
||||||
|
## possible values: return-original, return-query
|
||||||
|
# exit_mode = "return-original"
|
||||||
|
|
||||||
|
## possible values: emacs, subl
|
||||||
|
# word_jump_mode = "emacs"
|
||||||
|
|
||||||
|
## characters that count as a part of a word
|
||||||
|
# word_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
## number of context lines to show when scrolling by pages
|
||||||
|
## use ctrl instead of alt as the shortcut modifier key for numerical UI shortcuts
|
||||||
|
## alt-0 .. alt-9
|
||||||
|
# ctrl_n_shortcuts = false
|
||||||
|
|
||||||
|
## Show numeric shortcuts (1..9) beside list items in the TUI
|
||||||
|
## set to false to hide the moving numbers if you find them distracting
|
||||||
|
show_numeric_shortcuts = false
|
||||||
|
|
||||||
|
## default history list format - can also be specified with the --format arg
|
||||||
|
# history_format = "{time}\t{command}\t{duration}"
|
||||||
|
|
||||||
|
## prevent commands matching any of these regexes from being written to history.
|
||||||
|
## Note that these regular expressions are unanchored, i.e. if they don't start
|
||||||
|
## with ^ or end with $, they'll match anywhere in the command.
|
||||||
|
## For details on the supported regular expression syntax, see
|
||||||
|
## https://docs.rs/regex/latest/regex/#syntax
|
||||||
|
# history_filter = [
|
||||||
|
# "^secret-cmd",
|
||||||
|
# "^innocuous-cmd .*--secret=.+",
|
||||||
|
# ]
|
||||||
|
|
||||||
|
## prevent commands run with cwd matching any of these regexes from being written
|
||||||
|
## to history. Note that these regular expressions are unanchored, i.e. if they don't
|
||||||
|
## start with ^ or end with $, they'll match anywhere in CWD.
|
||||||
|
## For details on the supported regular expression syntax, see
|
||||||
|
## https://docs.rs/regex/latest/regex/#syntax
|
||||||
|
# cwd_filter = [
|
||||||
|
# "^/very/secret/area",
|
||||||
|
# ]
|
||||||
|
|
||||||
|
## Configure the maximum height of the preview to show.
|
||||||
|
## Useful when you have long scripts in your history that you want to distinguish
|
||||||
|
## by more than the first few lines.
|
||||||
|
# max_preview_height = 4
|
||||||
|
|
||||||
|
## Configure whether or not to show the help row, which includes the current Atuin
|
||||||
|
# show_help = true
|
||||||
|
|
||||||
|
## Configure whether or not to show tabs for search and inspect
|
||||||
|
# show_tabs = true
|
||||||
|
|
||||||
|
## Configure whether or not the tabs row may be auto-hidden, which includes the current Atuin
|
||||||
|
## tab, such as Search or Inspector, and other tabs you may wish to see. This will
|
||||||
|
## only be hidden if there are fewer than this count of lines available, and does not affect the use
|
||||||
|
## of keyboard shortcuts to switch tab. 0 to never auto-hide, default is 8 (lines).
|
||||||
|
## This is ignored except in `compact` mode.
|
||||||
|
# auto_hide_height = 8
|
||||||
|
|
||||||
|
## Defaults to true. This matches history against a set of default regex, and will not save it if we get a match. Defaults include
|
||||||
|
## 1. AWS key id
|
||||||
|
## 2. Github pat (old and new)
|
||||||
|
## 3. Slack oauth tokens (bot, user)
|
||||||
|
## 4. Slack webhooks
|
||||||
|
## 5. Stripe live/test keys
|
||||||
|
# secrets_filter = true
|
||||||
|
|
||||||
|
## Defaults to true. If enabled, upon hitting enter Atuin will immediately execute the command,
|
||||||
|
## whereas tab will put the command in the prompt for editing.
|
||||||
|
## If set to false, both enter and tab will place the command in the prompt for editing.
|
||||||
|
## This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
|
||||||
|
enter_accept = false
|
||||||
|
search_mode = "fuzzy"
|
||||||
|
|
||||||
|
## Defaults to false. If enabled, when triggered after &&, || or |, Atuin will complete commands to chain rather than replace the current line.
|
||||||
|
# command_chaining = false
|
||||||
|
|
||||||
|
## Defaults to "emacs". This specifies the keymap on the startup of `atuin
|
||||||
|
## search`. If this is set to "auto", the startup keymap mode in the Atuin
|
||||||
|
## search is automatically selected based on the shell's keymap where the
|
||||||
|
## keybinding is defined. If this is set to "emacs", "vim-insert", or
|
||||||
|
## "vim-normal", the startup keymap mode in the Atuin search is forced to be
|
||||||
|
## the specified one.
|
||||||
|
# keymap_mode = "auto"
|
||||||
|
|
||||||
|
## Cursor style in each keymap mode. If specified, the cursor style is changed
|
||||||
|
## in entering the cursor shape. Available values are "default" and
|
||||||
|
## "{blink,steady}-{block,underline,bar}".
|
||||||
|
# keymap_cursor = { emacs = "blink-block", vim_insert = "blink-block", vim_normal = "steady-block" }
|
||||||
|
|
||||||
|
# network_connect_timeout = 5
|
||||||
|
# network_timeout = 5
|
||||||
|
|
||||||
|
## Timeout (in seconds) for acquiring a local database connection (sqlite)
|
||||||
|
# local_timeout = 5
|
||||||
|
|
||||||
|
## Set this to true and Atuin will minimize motion in the UI - timers will not update live, etc.
|
||||||
|
## Alternatively, set env NO_MOTION=true
|
||||||
|
# prefers_reduced_motion = false
|
||||||
|
|
||||||
|
[stats]
|
||||||
|
## Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
|
||||||
|
# common_subcommands = [
|
||||||
|
# "apt",
|
||||||
|
# "cargo",
|
||||||
|
# "composer",
|
||||||
|
# "dnf",
|
||||||
|
# "docker",
|
||||||
|
# "dotnet",
|
||||||
|
# "git",
|
||||||
|
# "go",
|
||||||
|
# "ip",
|
||||||
|
# "jj",
|
||||||
|
# "kubectl",
|
||||||
|
# "nix",
|
||||||
|
# "nmcli",
|
||||||
|
# "npm",
|
||||||
|
# "pecl",
|
||||||
|
# "pnpm",
|
||||||
|
# "podman",
|
||||||
|
# "port",
|
||||||
|
# "systemctl",
|
||||||
|
# "tmux",
|
||||||
|
# "yarn",
|
||||||
|
# ]
|
||||||
|
|
||||||
|
## Set commands that should be totally stripped and ignored from stats
|
||||||
|
# common_prefix = ["sudo"]
|
||||||
|
|
||||||
|
## Set commands that will be completely ignored from stats
|
||||||
|
# ignored_commands = [
|
||||||
|
# "cd",
|
||||||
|
# "ls",
|
||||||
|
# "vi"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
[keys]
|
||||||
|
# Defaults to true. If disabled, using the up/down key won't exit the TUI when scrolled past the first/last entry.
|
||||||
|
# scroll_exits = true
|
||||||
|
|
||||||
|
# Defaults to true. The left arrow key will exit the TUI when scrolling before the first character
|
||||||
|
# exit_past_line_start = true
|
||||||
|
|
||||||
|
# Defaults to true. The right arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
|
||||||
|
# accept_past_line_end = true
|
||||||
|
|
||||||
|
# Defaults to false. The left arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
|
||||||
|
# accept_past_line_start = false
|
||||||
|
|
||||||
|
# Defaults to false. The backspace key performs the same functionality as Tab and copies the selected line to the command line to be modified when at the start of the line.
|
||||||
|
# accept_with_backspace = false
|
||||||
|
|
||||||
|
[sync]
|
||||||
|
# Enable sync v2 by default
|
||||||
|
# This ensures that sync v2 is enabled for new installs only
|
||||||
|
# In a later release it will become the default across the board
|
||||||
|
records = true
|
||||||
|
|
||||||
|
[preview]
|
||||||
|
## which preview strategy to use to calculate the preview height (respects max_preview_height).
|
||||||
|
## possible values: auto, static
|
||||||
|
## auto: length of the selected command.
|
||||||
|
## static: length of the longest command stored in the history.
|
||||||
|
## fixed: use max_preview_height as fixed height.
|
||||||
|
# strategy = "auto"
|
||||||
|
|
||||||
|
[daemon]
|
||||||
|
enabled = true
|
||||||
|
autostart = true
|
||||||
|
## Enables using the daemon to sync.
|
||||||
|
# enabled = false
|
||||||
|
|
||||||
|
## Automatically start and manage the daemon when needed.
|
||||||
|
## Not compatible with `systemd_socket = true`.
|
||||||
|
# autostart = false
|
||||||
|
|
||||||
|
## How often the daemon should sync in seconds
|
||||||
|
# sync_frequency = 300
|
||||||
|
|
||||||
|
## The path to the unix socket used by the daemon (on unix systems)
|
||||||
|
## linux/mac: ~/.local/share/atuin/atuin.sock
|
||||||
|
## windows: Not Supported
|
||||||
|
# socket_path = "~/.local/share/atuin/atuin.sock"
|
||||||
|
|
||||||
|
## The daemon pidfile used for lifecycle management.
|
||||||
|
## Defaults to the Atuin data directory.
|
||||||
|
# pidfile_path = "~/.local/share/atuin/atuin-daemon.pid"
|
||||||
|
|
||||||
|
## Use systemd socket activation rather than opening the given path (the path must still be correct for the client)
|
||||||
|
## linux: false
|
||||||
|
## mac/windows: Not Supported
|
||||||
|
# systemd_socket = false
|
||||||
|
|
||||||
|
## The port that should be used for TCP on non unix systems
|
||||||
|
# tcp_port = 8889
|
||||||
|
|
||||||
|
# [theme]
|
||||||
|
## Color theme to use for rendering in the terminal.
|
||||||
|
## There are some built-in themes, including the base theme ("default"),
|
||||||
|
## "autumn" and "marine". You can add your own themes to the "./themes" subdirectory of your
|
||||||
|
## Atuin config (or ATUIN_THEME_DIR, if provided) as TOML files whose keys should be one or
|
||||||
|
## more of AlertInfo, AlertWarn, AlertError, Annotation, Base, Guidance, Important, and
|
||||||
|
## the string values as lowercase entries from this list:
|
||||||
|
## https://ogeon.github.io/docs/palette/master/palette/named/index.html
|
||||||
|
## If you provide a custom theme file, it should be called "NAME.toml" and the theme below
|
||||||
|
## should be the stem, i.e. `theme = "NAME"` for your chosen NAME.
|
||||||
|
# name = "autumn"
|
||||||
|
|
||||||
|
## Whether the theme manager should output normal or extra information to help fix themes.
|
||||||
|
## Boolean, true or false. If unset, left up to the theme manager.
|
||||||
|
# debug = true
|
||||||
|
|
||||||
|
[search]
|
||||||
|
## The list of enabled filter modes, in order of priority.
|
||||||
|
## The "workspace" mode is skipped when not in a workspace or workspaces = false.
|
||||||
|
## Default filter mode can be overridden with the filter_mode setting.
|
||||||
|
# filters = [ "global", "host", "session", "session-preload", "workspace", "directory" ]
|
||||||
|
|
||||||
|
[tmux]
|
||||||
|
## Enable using atuin with tmux popup (requires tmux >= 3.2)
|
||||||
|
## When enabled and running inside tmux, Atuin will use a popup window for interactive search.
|
||||||
|
## Set to false to disable the popup.
|
||||||
|
## This can also be controlled with the ATUIN_TMUX_POPUP environment variable.
|
||||||
|
## Note: The tmux popup is currently supported in zsh, bash, and fish shells. This currently doesn't work with iTerm native tmux integration.
|
||||||
|
# enabled = false
|
||||||
|
|
||||||
|
## Width of the tmux popup window
|
||||||
|
## Can be a percentage, or integer (e.g. "100" means 100 characters wide)
|
||||||
|
# width = "80%"
|
||||||
|
|
||||||
|
## Height of the tmux popup window
|
||||||
|
## Can be a percentage, or integer (e.g. "100" means 100 lines tall)
|
||||||
|
# height = "60%"
|
||||||
|
|
||||||
|
[ui]
|
||||||
|
|
||||||
|
[ai]
|
||||||
|
enabled = true
|
||||||
|
## Columns to display in the interactive search, from left to right.
|
||||||
|
## The selection indicator (" > ") is always shown first implicitly.
|
||||||
|
##
|
||||||
|
## Each column can be specified as a simple string (uses default width)
|
||||||
|
## or as an object with type, width, and expand:
|
||||||
|
## { type = "directory", width = 30, expand = true }
|
||||||
|
##
|
||||||
|
## Available column types (with default widths):
|
||||||
|
## duration (5) - Command execution duration (e.g., "123ms")
|
||||||
|
## time (8) - Relative time since execution (e.g., "59m ago")
|
||||||
|
## datetime (16) - Absolute timestamp (e.g., "2025-01-22 14:35")
|
||||||
|
## directory (20) - Working directory (truncated if too long)
|
||||||
|
## host (15) - Hostname where command was run
|
||||||
|
## user (10) - Username
|
||||||
|
## exit (3) - Exit code (colored by success/failure)
|
||||||
|
## command (*) - The command itself (expands by default)
|
||||||
|
##
|
||||||
|
## The "expand" option (default: true for command, false for others) makes a
|
||||||
|
## column fill remaining space. Only one column should have expand = true.
|
||||||
|
##
|
||||||
|
## Default:
|
||||||
|
# columns = ["duration", "time", "command"]
|
||||||
|
##
|
||||||
|
## Examples:
|
||||||
|
##
|
||||||
|
## Minimal - more space for commands:
|
||||||
|
# columns = ["duration", "command"]
|
||||||
|
##
|
||||||
|
## With wider directory column:
|
||||||
|
# columns = ["duration", { type = "directory", width = 30 }, "command"]
|
||||||
|
##
|
||||||
|
## Show host for multi-machine sync users:
|
||||||
|
# columns = ["duration", "time", "host", "command"]
|
||||||
|
##
|
||||||
|
## Show exit codes prominently:
|
||||||
|
# columns = ["exit", "duration", "command"]
|
||||||
|
##
|
||||||
|
## Make directory expand instead of command:
|
||||||
|
# columns = ["duration", "time", { type = "directory", expand = true }, { type = "command", expand = false }]
|
||||||
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 790 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 778 KiB After Width: | Height: | Size: 778 KiB |
|
Before Width: | Height: | Size: 974 KiB After Width: | Height: | Size: 974 KiB |
|
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 430 KiB |
@@ -1,8 +1,8 @@
|
|||||||
source = $HOME/.config/hypr/mocha.conf
|
source = /home/liph/.config/hypr/mocha.conf
|
||||||
|
|
||||||
$accent = $mauve
|
$accent = $mauve
|
||||||
$accentAlpha = $mauveAlpha
|
$accentAlpha = $mauveAlpha
|
||||||
$font = JetBrainsMono Nerd Font
|
$font = Inconsolata Nerd Font
|
||||||
|
|
||||||
# GENERAL
|
# GENERAL
|
||||||
general {
|
general {
|
||||||
@@ -13,7 +13,7 @@ general {
|
|||||||
# BACKGROUND
|
# BACKGROUND
|
||||||
background {
|
background {
|
||||||
monitor =
|
monitor =
|
||||||
path = $WALLPAPER
|
path = /home/liph/.config/backgrounds/universe/magenta.jpg
|
||||||
blur_passes = 2
|
blur_passes = 2
|
||||||
color = $base
|
color = $base
|
||||||
}
|
}
|
||||||
@@ -2,3 +2,4 @@ wallpaper {
|
|||||||
monitor =
|
monitor =
|
||||||
path = $WALLPAPER
|
path = $WALLPAPER
|
||||||
}
|
}
|
||||||
|
splash = false
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
in vec2 v_texcoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 pixColor = texture(tex, v_texcoord);
|
||||||
|
|
||||||
|
// Adjust brightness
|
||||||
|
pixColor.rgb *= 1.00;
|
||||||
|
|
||||||
|
fragColor = pixColor;
|
||||||
|
}
|
||||||
@@ -13,14 +13,14 @@
|
|||||||
|
|
||||||
(defalias
|
(defalias
|
||||||
escctrl (tap-hold 250 250 esc lctrl)
|
escctrl (tap-hold 250 250 esc lctrl)
|
||||||
a (multi f24 (tap-hold $tap-time $hold-time a lmet))
|
a (tap-hold $tap-time $hold-time a lmet)
|
||||||
s (multi f24 (tap-hold $tap-time $hold-time s lalt))
|
s (tap-hold $tap-time $hold-time s lalt)
|
||||||
d (multi f24 (tap-hold $tap-time $hold-time d lsft))
|
d (tap-hold $tap-time $hold-time d lsft)
|
||||||
f (multi f24 (tap-hold $tap-time $hold-time f lctl))
|
f (tap-hold $tap-time $hold-time f lctl)
|
||||||
j (multi f24 (tap-hold $tap-time $hold-time j rctl))
|
j (tap-hold $tap-time $hold-time j rctl)
|
||||||
k (multi f24 (tap-hold $tap-time $hold-time k rsft))
|
k (tap-hold $tap-time $hold-time k rsft)
|
||||||
l (multi f24 (tap-hold $tap-time $hold-time l ralt))
|
l (tap-hold $tap-time $hold-time l ralt)
|
||||||
; (multi f24 (tap-hold $tap-time $hold-time ; rmet))
|
; (tap-hold $tap-time $hold-time ; rmet)
|
||||||
)
|
)
|
||||||
|
|
||||||
(deflayer base
|
(deflayer base
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/home/liph/.config/systemd/user/gammastep.service
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/home/liph/.config/systemd/user/kanata.service
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Gamma-colour filter (red-shift) for Wayland
|
||||||
|
After=graphical-session.target
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/gammastep -l 47.38:8.54 -t 6000:4000
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
[m[38:2:144:140:170;48:2:42:39:63m [38:2:109:128:134m [22;1;38:2:144:140:170mwob.service[22m
|
||||||
|
[m 1 [38:2:156:207:216m┆
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[38:2:110:106:134m~
|
||||||
|
[m[22;1;38:2:35:33:54;48:2:234:154:151m NORMAL [22;38:2:234:154:151;48:2:42:39:63m[38:2:224:222:244m wob.service [38:2:57:53:82m[38:2:234:154:151;48:2:57:53:82m Top [22;1;38:2:35:33:54;48:2:234:154:151m 1:1
|
||||||
@@ -10,13 +10,13 @@ Environment=DISPLAY=:0
|
|||||||
# WARNING: doing so will require the service to run as an elevated user such as root.
|
# WARNING: doing so will require the service to run as an elevated user such as root.
|
||||||
# Implementing least privilege access is an exercise left to the reader.
|
# Implementing least privilege access is an exercise left to the reader.
|
||||||
#
|
#
|
||||||
CPUSchedulingPolicy=rr
|
#CPUSchedulingPolicy=rr
|
||||||
CPUSchedulingPriority=99
|
#CPUSchedulingClass=realtim
|
||||||
IOSchedulingClass=realtime
|
|
||||||
Nice=-20
|
Nice=-20
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd"
|
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd --no-wait"
|
||||||
Restart=no
|
Restart=on failure
|
||||||
|
RestartkSec=3
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|||||||
@@ -1,80 +1,47 @@
|
|||||||
# vim:ft=kitty
|
background #282828
|
||||||
|
foreground #d4be98
|
||||||
|
|
||||||
## name: Catppuccin-Mocha
|
selection_background #d4be98
|
||||||
## author: Pocco81 (https://github.com/Pocco81)
|
selection_foreground #282828
|
||||||
## license: MIT
|
|
||||||
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
|
|
||||||
## blurb: Soothing pastel theme for the high-spirited!
|
|
||||||
|
|
||||||
window_padding_width 5 10
|
cursor #a89984
|
||||||
|
cursor_text_color background
|
||||||
|
|
||||||
# The basic colors
|
active_tab_background #282828
|
||||||
foreground #CDD6F4
|
active_tab_foreground #d4be98
|
||||||
background #1E1E2E
|
active_tab_font_style bold
|
||||||
selection_foreground #1E1E2E
|
inactive_tab_background #282828
|
||||||
selection_background #F5E0DC
|
inactive_tab_foreground #a89984
|
||||||
|
inactive_tab_font_style normal
|
||||||
|
|
||||||
# Cursor colors
|
# Black
|
||||||
cursor #F5E0DC
|
color0 #665c54
|
||||||
cursor_text_color #1E1E2E
|
color8 #928374
|
||||||
|
|
||||||
# URL underline color when hovering with mouse
|
# Red
|
||||||
url_color #F5E0DC
|
color1 #ea6962
|
||||||
|
color9 #ea6962
|
||||||
|
|
||||||
# Kitty window border colors
|
# Green
|
||||||
active_border_color #B4BEFE
|
color2 #a9b665
|
||||||
inactive_border_color #6C7086
|
color10 #a9b665
|
||||||
bell_border_color #F9E2AF
|
|
||||||
|
|
||||||
# OS Window titlebar colors
|
# Yellow
|
||||||
wayland_titlebar_color system
|
color3 #e78a4e
|
||||||
macos_titlebar_color system
|
color11 #d8a657
|
||||||
|
|
||||||
# Tab bar colors
|
# Blue
|
||||||
active_tab_foreground #11111B
|
color4 #7daea3
|
||||||
active_tab_background #CBA6F7
|
color12 #7daea3
|
||||||
inactive_tab_foreground #CDD6F4
|
|
||||||
inactive_tab_background #181825
|
|
||||||
tab_bar_background #11111B
|
|
||||||
|
|
||||||
# Colors for marks (marked text in the terminal)
|
# Magenta
|
||||||
mark1_foreground #1E1E2E
|
color5 #d3869b
|
||||||
mark1_background #B4BEFE
|
color13 #d3869b
|
||||||
mark2_foreground #1E1E2E
|
|
||||||
mark2_background #CBA6F7
|
|
||||||
mark3_foreground #1E1E2E
|
|
||||||
mark3_background #74C7EC
|
|
||||||
|
|
||||||
# The 16 terminal colors
|
# Cyan
|
||||||
|
color6 #89b482
|
||||||
|
color14 #89b482
|
||||||
|
|
||||||
# black
|
# White
|
||||||
color0 #45475A
|
color7 #d4be98
|
||||||
color8 #585B70
|
color15 #d4be98
|
||||||
|
|
||||||
# red
|
|
||||||
color1 #F38BA8
|
|
||||||
color9 #F38BA8
|
|
||||||
|
|
||||||
# green
|
|
||||||
color2 #A6E3A1
|
|
||||||
color10 #A6E3A1
|
|
||||||
|
|
||||||
# yellow
|
|
||||||
color3 #F9E2AF
|
|
||||||
color11 #F9E2AF
|
|
||||||
|
|
||||||
# blue
|
|
||||||
color4 #89B4FA
|
|
||||||
color12 #89B4FA
|
|
||||||
|
|
||||||
# magenta
|
|
||||||
color5 #F5C2E7
|
|
||||||
color13 #F5C2E7
|
|
||||||
|
|
||||||
# cyan
|
|
||||||
color6 #94E2D5
|
|
||||||
color14 #94E2D5
|
|
||||||
|
|
||||||
# white
|
|
||||||
color7 #BAC2DE
|
|
||||||
color15 #A6ADC8
|
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
# BEGIN_KITTY_THEME
|
# BEGIN_KITTY_THEME
|
||||||
# Catppuccin-Mocha
|
# Gruvbox Material
|
||||||
include current-theme.conf
|
include current-theme.conf
|
||||||
# END_KITTY_THEME
|
# END_KITTY_THEME
|
||||||
#
|
#
|
||||||
|
shell zsh --login
|
||||||
|
|
||||||
font_family Inconsolata Nerd Font
|
font_family JetBrainsMono Nerd Font
|
||||||
bold_font auto
|
bold_font auto
|
||||||
italic_font auto
|
italic_font auto
|
||||||
bold_italic_font auto
|
bold_italic_font auto
|
||||||
|
|
||||||
font_size 14
|
font_size 12
|
||||||
|
|
||||||
|
background_opacity 0.90
|
||||||
|
|
||||||
|
term xterm-256color
|
||||||
|
|
||||||
|
allow_hyperlinks yes
|
||||||
|
|
||||||
|
copy_on_select yes
|
||||||
|
paste_actions confirm-if-large
|
||||||
|
|
||||||
background_opacity 0.9
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# BEGIN_KITTY_THEME
|
||||||
|
# Kanagawa
|
||||||
|
include current-theme.conf
|
||||||
|
# END_KITTY_THEME
|
||||||
|
#
|
||||||
|
shell zsh --login
|
||||||
|
|
||||||
|
font_family JetBrainsMono Nerd Font
|
||||||
|
bold_font auto
|
||||||
|
italic_font auto
|
||||||
|
bold_italic_font auto
|
||||||
|
|
||||||
|
font_size 12
|
||||||
|
|
||||||
|
background_opacity 0.90
|
||||||
|
|
||||||
|
term xterm-256color
|
||||||
|
|
||||||
|
allow_hyperlinks yes
|
||||||
|
|
||||||
|
copy_on_select yes
|
||||||
|
paste_actions confirm-if-large
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# vim:ft=kitty
|
||||||
|
## name: Carbonfox
|
||||||
|
## author: EdenEast
|
||||||
|
## license: MIT
|
||||||
|
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/carbonfox/kitty.conf
|
||||||
|
## blurb: Carbonfox theme from the neovim colorscheme nightfox.nvim.
|
||||||
|
|
||||||
|
#: All the settings below are colors, which you can choose to modify, or use the
|
||||||
|
#: defaults. You can also add non-color based settings if needed but note that
|
||||||
|
#: these will not work with using kitty @ set-colors with this theme. For a
|
||||||
|
#: reference on what these settings do see https://sw.kovidgoyal.net/kitty/conf/
|
||||||
|
|
||||||
|
#: The basic colors
|
||||||
|
|
||||||
|
foreground #f2f4f8
|
||||||
|
background #161616
|
||||||
|
selection_foreground #f2f4f8
|
||||||
|
selection_background #2a2a2a
|
||||||
|
|
||||||
|
|
||||||
|
#: Cursor colors
|
||||||
|
|
||||||
|
cursor #f2f4f8
|
||||||
|
cursor_text_color #161616
|
||||||
|
|
||||||
|
|
||||||
|
#: URL underline color when hovering with mouse
|
||||||
|
|
||||||
|
url_color #25be6a
|
||||||
|
|
||||||
|
|
||||||
|
#: kitty window border colors and terminal bell colors
|
||||||
|
|
||||||
|
active_border_color #78a9ff
|
||||||
|
inactive_border_color #535353
|
||||||
|
bell_border_color #3ddbd9
|
||||||
|
# visual_bell_color none
|
||||||
|
|
||||||
|
|
||||||
|
#: OS Window titlebar colors
|
||||||
|
|
||||||
|
# wayland_titlebar_color system
|
||||||
|
# macos_titlebar_color system
|
||||||
|
|
||||||
|
|
||||||
|
#: Tab bar colors
|
||||||
|
|
||||||
|
active_tab_foreground #0c0c0c
|
||||||
|
active_tab_background #78a9ff
|
||||||
|
inactive_tab_foreground #6e6f70
|
||||||
|
inactive_tab_background #2a2a2a
|
||||||
|
# tab_bar_background none
|
||||||
|
# tab_bar_margin_color none
|
||||||
|
|
||||||
|
|
||||||
|
#: Colors for marks (marked text in the terminal)
|
||||||
|
|
||||||
|
# mark1_foreground black
|
||||||
|
# mark1_background #98d3cb
|
||||||
|
# mark2_foreground black
|
||||||
|
# mark2_background #f2dcd3
|
||||||
|
# mark3_foreground black
|
||||||
|
# mark3_background #f274bc
|
||||||
|
|
||||||
|
|
||||||
|
#: The basic 16 colors
|
||||||
|
|
||||||
|
#: black
|
||||||
|
color0 #282828
|
||||||
|
color8 #484848
|
||||||
|
|
||||||
|
#: red
|
||||||
|
color1 #ee5396
|
||||||
|
color9 #f16da6
|
||||||
|
|
||||||
|
#: green
|
||||||
|
color2 #25be6a
|
||||||
|
color10 #46c880
|
||||||
|
|
||||||
|
#: yellow
|
||||||
|
color3 #ebcb8b
|
||||||
|
color11 #f0d399
|
||||||
|
|
||||||
|
#: blue
|
||||||
|
color4 #78a9ff
|
||||||
|
color12 #8cb6ff
|
||||||
|
|
||||||
|
#: magenta
|
||||||
|
color5 #be95ff
|
||||||
|
color13 #c8a5ff
|
||||||
|
|
||||||
|
#: cyan
|
||||||
|
color6 #33b1ff
|
||||||
|
color14 #52bdff
|
||||||
|
|
||||||
|
#: white
|
||||||
|
color7 #dfdfe0
|
||||||
|
color15 #e4e4e5
|
||||||
|
|
||||||
|
|
||||||
|
#: You can set the remaining 240 colors as color16 to color255.
|
||||||
|
|
||||||
|
color16 #3ddbd9
|
||||||
|
color17 #ff7eb6
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# vim:ft=kitty
|
||||||
|
|
||||||
|
## name: Catppuccin-Mocha
|
||||||
|
## author: Pocco81 (https://github.com/Pocco81)
|
||||||
|
## license: MIT
|
||||||
|
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
|
||||||
|
## blurb: Soothing pastel theme for the high-spirited!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# The basic colors
|
||||||
|
foreground #CDD6F4
|
||||||
|
background #1E1E2E
|
||||||
|
selection_foreground #1E1E2E
|
||||||
|
selection_background #F5E0DC
|
||||||
|
|
||||||
|
# Cursor colors
|
||||||
|
cursor #F5E0DC
|
||||||
|
cursor_text_color #1E1E2E
|
||||||
|
|
||||||
|
# URL underline color when hovering with mouse
|
||||||
|
url_color #F5E0DC
|
||||||
|
|
||||||
|
# Kitty window border colors
|
||||||
|
active_border_color #B4BEFE
|
||||||
|
inactive_border_color #6C7086
|
||||||
|
bell_border_color #F9E2AF
|
||||||
|
|
||||||
|
# OS Window titlebar colors
|
||||||
|
wayland_titlebar_color system
|
||||||
|
macos_titlebar_color system
|
||||||
|
|
||||||
|
# Tab bar colors
|
||||||
|
active_tab_foreground #11111B
|
||||||
|
active_tab_background #CBA6F7
|
||||||
|
inactive_tab_foreground #CDD6F4
|
||||||
|
inactive_tab_background #181825
|
||||||
|
tab_bar_background #11111B
|
||||||
|
|
||||||
|
# Colors for marks (marked text in the terminal)
|
||||||
|
mark1_foreground #1E1E2E
|
||||||
|
mark1_background #B4BEFE
|
||||||
|
mark2_foreground #1E1E2E
|
||||||
|
mark2_background #CBA6F7
|
||||||
|
mark3_foreground #1E1E2E
|
||||||
|
mark3_background #74C7EC
|
||||||
|
|
||||||
|
# The 16 terminal colors
|
||||||
|
|
||||||
|
# black
|
||||||
|
color0 #45475A
|
||||||
|
color8 #585B70
|
||||||
|
|
||||||
|
# red
|
||||||
|
color1 #F38BA8
|
||||||
|
color9 #F38BA8
|
||||||
|
|
||||||
|
# green
|
||||||
|
color2 #A6E3A1
|
||||||
|
color10 #A6E3A1
|
||||||
|
|
||||||
|
# yellow
|
||||||
|
color3 #F9E2AF
|
||||||
|
color11 #F9E2AF
|
||||||
|
|
||||||
|
# blue
|
||||||
|
color4 #89B4FA
|
||||||
|
color12 #89B4FA
|
||||||
|
|
||||||
|
# magenta
|
||||||
|
color5 #F5C2E7
|
||||||
|
color13 #F5C2E7
|
||||||
|
|
||||||
|
# cyan
|
||||||
|
color6 #94E2D5
|
||||||
|
color14 #94E2D5
|
||||||
|
|
||||||
|
# white
|
||||||
|
color7 #BAC2DE
|
||||||
|
color15 #A6ADC8
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# https://draculatheme.com/kitty
|
||||||
|
#
|
||||||
|
# Installation instructions:
|
||||||
|
#
|
||||||
|
# cp dracula.conf ~/.config/kitty/
|
||||||
|
# echo "include dracula.conf" >> ~/.config/kitty/kitty.conf
|
||||||
|
#
|
||||||
|
# Then reload kitty for the config to take affect.
|
||||||
|
# Alternatively copy paste below directly into kitty.conf
|
||||||
|
|
||||||
|
foreground #f8f8f2
|
||||||
|
background #282a36
|
||||||
|
selection_foreground #ffffff
|
||||||
|
selection_background #44475a
|
||||||
|
|
||||||
|
url_color #8be9fd
|
||||||
|
|
||||||
|
# black
|
||||||
|
color0 #21222c
|
||||||
|
color8 #6272a4
|
||||||
|
|
||||||
|
# red
|
||||||
|
color1 #ff5555
|
||||||
|
color9 #ff6e6e
|
||||||
|
|
||||||
|
# green
|
||||||
|
color2 #50fa7b
|
||||||
|
color10 #69ff94
|
||||||
|
|
||||||
|
# yellow
|
||||||
|
color3 #f1fa8c
|
||||||
|
color11 #ffffa5
|
||||||
|
|
||||||
|
# blue
|
||||||
|
color4 #bd93f9
|
||||||
|
color12 #d6acff
|
||||||
|
|
||||||
|
# magenta
|
||||||
|
color5 #ff79c6
|
||||||
|
color13 #ff92df
|
||||||
|
|
||||||
|
# cyan
|
||||||
|
color6 #8be9fd
|
||||||
|
color14 #a4ffff
|
||||||
|
|
||||||
|
# white
|
||||||
|
color7 #f8f8f2
|
||||||
|
color15 #ffffff
|
||||||
|
|
||||||
|
# Cursor colors
|
||||||
|
cursor #f8f8f2
|
||||||
|
cursor_text_color background
|
||||||
|
|
||||||
|
# Tab bar colors
|
||||||
|
active_tab_foreground #282a36
|
||||||
|
active_tab_background #f8f8f2
|
||||||
|
inactive_tab_foreground #282a36
|
||||||
|
inactive_tab_background #6272a4
|
||||||
|
|
||||||
|
# Marks
|
||||||
|
mark1_foreground #282a36
|
||||||
|
mark1_background #ff5555
|
||||||
|
|
||||||
|
# Splits/Windows
|
||||||
|
active_border_color #f8f8f2
|
||||||
|
inactive_border_color #6272a4
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Color theme: Everforest Dark Medium
|
||||||
|
# Auto-generated by Gogh (https://Gogh-Co.github.io/Gogh/)
|
||||||
|
|
||||||
|
color0 #343F44
|
||||||
|
color1 #E67E80
|
||||||
|
color2 #A7C080
|
||||||
|
color3 #DBBC7F
|
||||||
|
color4 #7FBBB3
|
||||||
|
color5 #D699B6
|
||||||
|
color6 #83C092
|
||||||
|
color7 #D3C6AA
|
||||||
|
color8 #5C6A72
|
||||||
|
color9 #F85552
|
||||||
|
color10 #8DA101
|
||||||
|
color11 #DFA000
|
||||||
|
color12 #3A94C5
|
||||||
|
color13 #DF69BA
|
||||||
|
color14 #35A77C
|
||||||
|
color15 #DFDDC8
|
||||||
|
background #2D353B
|
||||||
|
foreground #D3C6AA
|
||||||
|
cursor #D3C6AA
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
background #282828
|
||||||
|
foreground #d4be98
|
||||||
|
|
||||||
|
selection_background #d4be98
|
||||||
|
selection_foreground #282828
|
||||||
|
|
||||||
|
cursor #a89984
|
||||||
|
cursor_text_color background
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Black
|
||||||
|
color0 #665c54
|
||||||
|
color8 #928374
|
||||||
|
|
||||||
|
# Red
|
||||||
|
color1 #ea6962
|
||||||
|
color9 #ea6962
|
||||||
|
|
||||||
|
# Green
|
||||||
|
color2 #a9b665
|
||||||
|
color10 #a9b665
|
||||||
|
|
||||||
|
# Yellow
|
||||||
|
color3 #e78a4e
|
||||||
|
color11 #d8a657
|
||||||
|
|
||||||
|
# Blue
|
||||||
|
color4 #7daea3
|
||||||
|
color12 #7daea3
|
||||||
|
|
||||||
|
# Magenta
|
||||||
|
color5 #d3869b
|
||||||
|
color13 #d3869b
|
||||||
|
|
||||||
|
# Cyan
|
||||||
|
color6 #89b482
|
||||||
|
color14 #89b482
|
||||||
|
|
||||||
|
# White
|
||||||
|
color7 #d4be98
|
||||||
|
color15 #d4be98
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# vim:ft=kitty
|
||||||
|
|
||||||
|
## name: Kanagawa
|
||||||
|
## license: MIT
|
||||||
|
## author: Tommaso Laurenzi
|
||||||
|
## upstream: https://github.com/rebelot/kanagawa.nvim/
|
||||||
|
|
||||||
|
|
||||||
|
background #1F1F28
|
||||||
|
foreground #DCD7BA
|
||||||
|
selection_background #2D4F67
|
||||||
|
selection_foreground #C8C093
|
||||||
|
url_color #72A7BC
|
||||||
|
cursor #C8C093
|
||||||
|
|
||||||
|
# Tabs
|
||||||
|
active_tab_background #1F1F28
|
||||||
|
active_tab_foreground #C8C093
|
||||||
|
inactive_tab_background #1F1F28
|
||||||
|
inactive_tab_foreground #727169
|
||||||
|
#tab_bar_background #15161E
|
||||||
|
|
||||||
|
# normal
|
||||||
|
color0 #16161D
|
||||||
|
color1 #C34043
|
||||||
|
color2 #76946A
|
||||||
|
color3 #C0A36E
|
||||||
|
color4 #7E9CD8
|
||||||
|
color5 #957FB8
|
||||||
|
color6 #6A9589
|
||||||
|
color7 #C8C093
|
||||||
|
|
||||||
|
# bright
|
||||||
|
color8 #727169
|
||||||
|
color9 #E82424
|
||||||
|
color10 #98BB6C
|
||||||
|
color11 #E6C384
|
||||||
|
color12 #7FB4CA
|
||||||
|
color13 #938AA9
|
||||||
|
color14 #7AA89F
|
||||||
|
color15 #DCD7BA
|
||||||
|
|
||||||
|
|
||||||
|
# extended colors
|
||||||
|
color16 #FFA066
|
||||||
|
color17 #FF5D62
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# vim:ft=kitty
|
||||||
|
|
||||||
|
## name: Nightfox
|
||||||
|
## author: EdenEast
|
||||||
|
## license: MIT
|
||||||
|
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/nightfox/nightfox_kitty.conf
|
||||||
|
|
||||||
|
background #192330
|
||||||
|
foreground #cdcecf
|
||||||
|
selection_background #2b3b51
|
||||||
|
selection_foreground #cdcecf
|
||||||
|
url_color #81b29a
|
||||||
|
|
||||||
|
# Cursor
|
||||||
|
# uncomment for reverse background
|
||||||
|
# cursor none
|
||||||
|
cursor #cdcecf
|
||||||
|
|
||||||
|
# Border
|
||||||
|
active_border_color #719cd6
|
||||||
|
inactive_border_color #39506d
|
||||||
|
bell_border_color #f4a261
|
||||||
|
|
||||||
|
# Tabs
|
||||||
|
active_tab_background #719cd6
|
||||||
|
active_tab_foreground #131a24
|
||||||
|
inactive_tab_background #2b3b51
|
||||||
|
inactive_tab_foreground #738091
|
||||||
|
|
||||||
|
# normal
|
||||||
|
color0 #393b44
|
||||||
|
color1 #c94f6d
|
||||||
|
color2 #81b29a
|
||||||
|
color3 #dbc074
|
||||||
|
color4 #719cd6
|
||||||
|
color5 #9d79d6
|
||||||
|
color6 #63cdcf
|
||||||
|
color7 #dfdfe0
|
||||||
|
|
||||||
|
# bright
|
||||||
|
color8 #575860
|
||||||
|
color9 #d16983
|
||||||
|
color10 #8ebaa4
|
||||||
|
color11 #e0c989
|
||||||
|
color12 #86abdc
|
||||||
|
color13 #baa1e2
|
||||||
|
color14 #7ad5d6
|
||||||
|
color15 #e4e4e5
|
||||||
|
|
||||||
|
# extended colors
|
||||||
|
color16 #f4a261
|
||||||
|
color17 #d67ad2
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# One Dark by Giuseppe Cesarano, https://github.com/GiuseppeCesarano
|
||||||
|
# This work is licensed under the terms of the GPL-2.0 license.
|
||||||
|
# For a copy, see https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
|
||||||
|
foreground #979eab
|
||||||
|
background #282c34
|
||||||
|
|
||||||
|
color0 #282c34
|
||||||
|
color1 #e06c75
|
||||||
|
color2 #98c379
|
||||||
|
color3 #e5c07b
|
||||||
|
color4 #61afef
|
||||||
|
color5 #be5046
|
||||||
|
color6 #56b6c2
|
||||||
|
color7 #979eab
|
||||||
|
color8 #393e48
|
||||||
|
color9 #d19a66
|
||||||
|
color10 #56b6c2
|
||||||
|
color11 #e5c07b
|
||||||
|
color12 #61afef
|
||||||
|
color13 #be5046
|
||||||
|
color14 #56b6c2
|
||||||
|
color15 #abb2bf
|
||||||
|
|
||||||
|
# Tab Bar
|
||||||
|
|
||||||
|
active_tab_foreground #282c34
|
||||||
|
active_tab_background #979eab
|
||||||
|
inactive_tab_foreground #abb2bf
|
||||||
|
inactive_tab_background #282c34
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
## name: Rosé Pine Moon
|
||||||
|
## author: mvllow
|
||||||
|
## 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
|
||||||
|
background #232136
|
||||||
|
selection_foreground #e0def4
|
||||||
|
selection_background #44415a
|
||||||
|
|
||||||
|
cursor #56526e
|
||||||
|
cursor_text_color #e0def4
|
||||||
|
|
||||||
|
url_color #c4a7e7
|
||||||
|
|
||||||
|
active_tab_foreground #e0def4
|
||||||
|
active_tab_background #393552
|
||||||
|
inactive_tab_foreground #6e6a86
|
||||||
|
inactive_tab_background #232136
|
||||||
|
|
||||||
|
active_border_color #3e8fb0
|
||||||
|
inactive_border_color #44415a
|
||||||
|
|
||||||
|
# black
|
||||||
|
color0 #393552
|
||||||
|
color8 #6e6a86
|
||||||
|
|
||||||
|
# red
|
||||||
|
color1 #eb6f92
|
||||||
|
color9 #eb6f92
|
||||||
|
|
||||||
|
# green
|
||||||
|
color2 #3e8fb0
|
||||||
|
color10 #3e8fb0
|
||||||
|
|
||||||
|
# yellow
|
||||||
|
color3 #f6c177
|
||||||
|
color11 #f6c177
|
||||||
|
|
||||||
|
# blue
|
||||||
|
color4 #9ccfd8
|
||||||
|
color12 #9ccfd8
|
||||||
|
|
||||||
|
# magenta
|
||||||
|
color5 #c4a7e7
|
||||||
|
color13 #c4a7e7
|
||||||
|
|
||||||
|
# cyan
|
||||||
|
color6 #ea9a97
|
||||||
|
color14 #ea9a97
|
||||||
|
|
||||||
|
# white
|
||||||
|
color7 #e0def4
|
||||||
|
color15 #e0def4
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# vim:ft=kitty
|
||||||
|
|
||||||
|
## name: Sonokai Sushia
|
||||||
|
## license: MIT
|
||||||
|
## author: Dustin Miao
|
||||||
|
## upstream: https://github.com/selenebun/sonokai-kitty
|
||||||
|
|
||||||
|
## blurb: A Kitty port for the Sonokai theme (https://github.com/sainnhe/sonokai)
|
||||||
|
|
||||||
|
#: Disclaimer: I am not the original creator of this theme. I have ported it over from
|
||||||
|
#: https://github.com/selenebun/sonokai-kitty/blob/main/colors/sonokai-shusia.conf.
|
||||||
|
|
||||||
|
foreground #e3e1e4
|
||||||
|
background #2d2a2e
|
||||||
|
|
||||||
|
selection_foreground #e3e1e4
|
||||||
|
selection_background #423f46
|
||||||
|
|
||||||
|
cursor #e3e1e4
|
||||||
|
cursor_text_color background
|
||||||
|
|
||||||
|
active_tab_background #2d2a2e
|
||||||
|
active_tab_foreground #e3e1e4
|
||||||
|
active_tab_font_style bold
|
||||||
|
inactive_tab_background #2d2a2e
|
||||||
|
inactive_tab_foreground #e3e1e4
|
||||||
|
inactive_tab_font_style normal
|
||||||
|
|
||||||
|
color0 #1a181a
|
||||||
|
color8 #848089
|
||||||
|
|
||||||
|
color1 #f85e84
|
||||||
|
color9 #f85e84
|
||||||
|
|
||||||
|
color2 #9ecd6f
|
||||||
|
color10 #9ecd6f
|
||||||
|
|
||||||
|
color3 #e5c463
|
||||||
|
color11 #e5c463
|
||||||
|
|
||||||
|
color4 #7accd7
|
||||||
|
color12 #7accd7
|
||||||
|
|
||||||
|
color5 #ab9df2
|
||||||
|
color13 #ab9df2
|
||||||
|
|
||||||
|
color6 #ef9062
|
||||||
|
color7 #ef9062
|
||||||
|
|
||||||
|
color7 #e3e1e4
|
||||||
|
color15 #e3e1e4
|
||||||
@@ -0,0 +1,614 @@
|
|||||||
|
// This config is in the KDL format: https://kdl.dev
|
||||||
|
// "/-" comments out the following node.
|
||||||
|
// Check the wiki for a full description of the configuration:
|
||||||
|
// https://yalter.github.io/niri/Configuration:-Introduction
|
||||||
|
input {
|
||||||
|
keyboard {
|
||||||
|
|
||||||
|
xkb {
|
||||||
|
layout "ch"
|
||||||
|
variant "de_mac"
|
||||||
|
model "apple"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
touchpad {
|
||||||
|
tap
|
||||||
|
natural-scroll
|
||||||
|
}
|
||||||
|
mouse {
|
||||||
|
|
||||||
|
}
|
||||||
|
trackpoint {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x11 support for wayland
|
||||||
|
xwayland-satellite {
|
||||||
|
path "xwayland-satellite"
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
ELECTRON_OZONE_PLATFORM_HINT "auto"
|
||||||
|
ELECTRON_ENABLE_LOGGING "1"
|
||||||
|
// GDK_SCALE "1.75"
|
||||||
|
// GDK_DPI_SCALE "1"
|
||||||
|
}
|
||||||
|
output "eDP-1" {
|
||||||
|
mode "2880x1800@59.990"
|
||||||
|
scale 1.1
|
||||||
|
position x=0 y=0
|
||||||
|
focus-at-startup
|
||||||
|
}
|
||||||
|
output "DP-4" {
|
||||||
|
mode "2560x1440@59.951"
|
||||||
|
position x=1645 y=0
|
||||||
|
scale 0.8
|
||||||
|
}
|
||||||
|
layout {
|
||||||
|
gaps 2
|
||||||
|
center-focused-column "never"
|
||||||
|
preset-column-widths {
|
||||||
|
proportion 0.5
|
||||||
|
proportion 0.66667
|
||||||
|
proportion 0.3333
|
||||||
|
}
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.5
|
||||||
|
}
|
||||||
|
focus-ring {
|
||||||
|
width 2
|
||||||
|
active-color "#7daea3"
|
||||||
|
inactive-color "#928374"
|
||||||
|
}
|
||||||
|
border {
|
||||||
|
off
|
||||||
|
width 4
|
||||||
|
active-color "#eba0ac"
|
||||||
|
inactive-color "#505050"
|
||||||
|
urgent-color "#9b0000"
|
||||||
|
}
|
||||||
|
shadow {
|
||||||
|
softness 30
|
||||||
|
spread 5
|
||||||
|
offset x=0 y=5
|
||||||
|
color "#0007"
|
||||||
|
}
|
||||||
|
struts {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn-at-startup "bash" "/mnt/tank/scripts/fix-bitwarden.sh"
|
||||||
|
spawn-at-startup "waybar" "-c" "/home/liph/.config/niri/waybar-niri/config.jsonc" "-s" "/home/liph/.config/niri/waybar-niri/style.css"
|
||||||
|
spawn-at-startup "swaync"
|
||||||
|
// spawn-at-startup "kanshi"
|
||||||
|
spawn-at-startup "awww-daemon"
|
||||||
|
spawn-at-startup "xwayland-satellite"
|
||||||
|
spawn-at-startup "gammastep" "-l" "47.38:8.54" "-t" "6000:4000"
|
||||||
|
spawn-at-startup "wezterm"
|
||||||
|
spawn-at-startup "obsidian"
|
||||||
|
// spawn-at-startup "kanshi"
|
||||||
|
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'"
|
||||||
|
|
||||||
|
prefer-no-csd
|
||||||
|
gestures {
|
||||||
|
hot-corners {
|
||||||
|
off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace "1"
|
||||||
|
workspace "2"
|
||||||
|
workspace "3"
|
||||||
|
workspace "4"
|
||||||
|
|
||||||
|
// Example: block out two password managers from screen capture.
|
||||||
|
// (This example rule is commented out with a "/-" in front.)
|
||||||
|
/-window-rule {
|
||||||
|
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
|
||||||
|
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
||||||
|
block-out-from "screen-capture"
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="Zoom Workplace"
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
// Wezterm window rules
|
||||||
|
window-rule {
|
||||||
|
match at-startup=true app-id="wezterm"
|
||||||
|
open-on-workspace "1"
|
||||||
|
open-maximized true
|
||||||
|
open-floating false
|
||||||
|
// clip-to-geometry true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match at-startup=false app-id="wezterm"
|
||||||
|
open-floating false
|
||||||
|
// clip-to-geometry true
|
||||||
|
}
|
||||||
|
// Obsidian window rules
|
||||||
|
window-rule {
|
||||||
|
match at-startup=true app-id="obsidian"
|
||||||
|
open-on-workspace "3"
|
||||||
|
open-maximized true
|
||||||
|
open-floating false
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match at-startup=false app-id="obsidian"
|
||||||
|
open-maximized true
|
||||||
|
open-floating false
|
||||||
|
}
|
||||||
|
// Librewolf window rules
|
||||||
|
window-rule {
|
||||||
|
match at-startup=true app-id="librewolf"
|
||||||
|
open-on-workspace "2"
|
||||||
|
open-maximized true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="librewolf"
|
||||||
|
open-maximized true
|
||||||
|
clip-to-geometry true
|
||||||
|
open-floating false
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="Tor Browser"
|
||||||
|
open-maximized true
|
||||||
|
clip-to-geometry true
|
||||||
|
open-floating false
|
||||||
|
open-on-workspace "2"
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="Claude"
|
||||||
|
open-on-workspace "4"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
clip-to-geometry true
|
||||||
|
}
|
||||||
|
// 2. Specific rule for windows opening at startup
|
||||||
|
window-rule {
|
||||||
|
match app-id="eu.betterbird.Betterbird"
|
||||||
|
open-maximized true
|
||||||
|
clip-to-geometry true
|
||||||
|
open-on-workspace "3"
|
||||||
|
}
|
||||||
|
|
||||||
|
window-rule {
|
||||||
|
match app-id="Slack"
|
||||||
|
open-maximized true
|
||||||
|
clip-to-geometry true
|
||||||
|
open-on-workspace "4"
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_btop"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_htop"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_bluetui"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_pulsemixer"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_wavemon"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_network-tui"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
match app-id="floating_yazi"
|
||||||
|
default-column-width {
|
||||||
|
proportion 0.6667
|
||||||
|
}
|
||||||
|
open-floating true
|
||||||
|
}
|
||||||
|
window-rule {
|
||||||
|
geometry-corner-radius 5
|
||||||
|
clip-to-geometry true
|
||||||
|
}
|
||||||
|
binds {
|
||||||
|
// Mod+Shift+Slash { show-hotkey-overlay; }
|
||||||
|
Mod+Mod5+J {
|
||||||
|
spawn "hyprlock"
|
||||||
|
}
|
||||||
|
Mod+Alt+P hotkey-overlay-title="Open Bitwarden" {
|
||||||
|
spawn "bitwarden-desktop"
|
||||||
|
}
|
||||||
|
Mod+Alt+T hotkey-overlay-title="Open Tipp10" {
|
||||||
|
spawn "tipp10"
|
||||||
|
}
|
||||||
|
Mod+Alt+S hotkey-overlay-title="Open Tidal" {
|
||||||
|
spawn "tidal-hifi" "--ozone-platform=wayland" "--enable-features=WaylandWindowDecorations"
|
||||||
|
}
|
||||||
|
Mod+Alt+O hotkey-overlay-title="Open Obsidian" {
|
||||||
|
spawn "obsidian"
|
||||||
|
}
|
||||||
|
Mod+Alt+C hotkey-overlay-title="Open Claude" {
|
||||||
|
spawn "claude-desktop"
|
||||||
|
}
|
||||||
|
Mod+Alt+Z hotkey-overlay-title="Open Librewolf" {
|
||||||
|
spawn "librewolf"
|
||||||
|
}
|
||||||
|
Mod+Alt+Return hotkey-overlay-title="Open Kitty" {
|
||||||
|
spawn "wezterm"
|
||||||
|
}
|
||||||
|
Mod+Alt+Space hotkey-overlay-title="Open Wofi" {
|
||||||
|
spawn "wofi" "--show" "drun"
|
||||||
|
}
|
||||||
|
Mod+Mod5+Q hotkey-overlay-title="Open Htop" {
|
||||||
|
spawn "kitty" "--class" "floating_htop" "-e" "htop"
|
||||||
|
}
|
||||||
|
Mod+Mod5+W hotkey-overlay-title="Open Btop" {
|
||||||
|
spawn "kitty" "--class" "floating_btop" "-e" "btop"
|
||||||
|
}
|
||||||
|
Mod+Mod5+E hotkey-overlay-title="Open Pulsemixer" {
|
||||||
|
spawn "kitty" "--class" "floating_pulsemixer" "-e" "pulsemixer"
|
||||||
|
}
|
||||||
|
Mod+Mod5+R allow-inhibiting=false hotkey-overlay-title="Open Bluetui" {
|
||||||
|
spawn "kitty" "--class" "floating_bluetui" "-e" "bluetui"
|
||||||
|
}
|
||||||
|
Mod+Mod5+T hotkey-overlay-title="Open 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"
|
||||||
|
}
|
||||||
|
Mod+Mod5+Y hotkey-overlay-title="Open Yazi" {
|
||||||
|
spawn "kitty" "--class" "floating_yazi" "-e" "yazi"
|
||||||
|
}
|
||||||
|
// Brightness Controll
|
||||||
|
Mod+Ctrl+0 {
|
||||||
|
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+1 {
|
||||||
|
spawn "sh" "-c" "~/scripts/layer_notify.sh 1"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+2 {
|
||||||
|
spawn "sh" "-c" "~/scripts/layer_notify.sh 2"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+3 {
|
||||||
|
spawn "sh" "-c" "~/scripts/layer_notify.sh 3"
|
||||||
|
}
|
||||||
|
// Brightness controls
|
||||||
|
Mod+Ctrl+4 {
|
||||||
|
spawn "sh" "-c" "~/scripts/niri_br_up.sh"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+5 {
|
||||||
|
spawn "sh" "-c" "~/scripts/niri_br_down.sh"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+6 {
|
||||||
|
spawn "sh" "-c" "~/scripts/niri_br_blue.sh"
|
||||||
|
}
|
||||||
|
Mod+Ctrl+7 {
|
||||||
|
spawn "sh" "-c" "~/scripts/niri_br_reset.sh"
|
||||||
|
}
|
||||||
|
// Swaync Notifications
|
||||||
|
Mod+Ctrl+8 {
|
||||||
|
spawn "swaync-client" "-t"
|
||||||
|
}
|
||||||
|
Super+Alt+S allow-when-locked=true hotkey-overlay-title=null {
|
||||||
|
spawn-sh "pkill orca || exec orca"
|
||||||
|
}
|
||||||
|
XF86AudioRaiseVolume allow-when-locked=true {
|
||||||
|
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+ -l 1.0"
|
||||||
|
}
|
||||||
|
XF86AudioLowerVolume allow-when-locked=true {
|
||||||
|
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"
|
||||||
|
}
|
||||||
|
XF86AudioMute allow-when-locked=true {
|
||||||
|
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
||||||
|
}
|
||||||
|
XF86AudioMicMute allow-when-locked=true {
|
||||||
|
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
|
||||||
|
}
|
||||||
|
XF86AudioPlay allow-when-locked=true {
|
||||||
|
spawn-sh "playerctl play-pause"
|
||||||
|
}
|
||||||
|
XF86AudioStop allow-when-locked=true {
|
||||||
|
spawn-sh "playerctl stop"
|
||||||
|
}
|
||||||
|
XF86AudioPrev allow-when-locked=true {
|
||||||
|
spawn-sh "playerctl previous"
|
||||||
|
}
|
||||||
|
XF86AudioNext allow-when-locked=true {
|
||||||
|
spawn-sh "playerctl next"
|
||||||
|
}
|
||||||
|
XF86MonBrightnessUp allow-when-locked=true {
|
||||||
|
spawn "brightnessctl" "--class=backlight" "set" "+10%"
|
||||||
|
}
|
||||||
|
XF86MonBrightnessDown allow-when-locked=true {
|
||||||
|
spawn "brightnessctl" "--class=backlight" "set" "10%-"
|
||||||
|
}
|
||||||
|
Mod+O repeat=false {
|
||||||
|
toggle-overview
|
||||||
|
}
|
||||||
|
Mod+Shift+C repeat=false {
|
||||||
|
close-window
|
||||||
|
}
|
||||||
|
Mod+Left {
|
||||||
|
focus-column-left
|
||||||
|
}
|
||||||
|
Mod+Down {
|
||||||
|
focus-window-down
|
||||||
|
}
|
||||||
|
Mod+Up {
|
||||||
|
focus-window-up
|
||||||
|
}
|
||||||
|
Mod+Right {
|
||||||
|
focus-column-right
|
||||||
|
}
|
||||||
|
Mod+H {
|
||||||
|
focus-column-left
|
||||||
|
}
|
||||||
|
Mod+J {
|
||||||
|
focus-window-down
|
||||||
|
}
|
||||||
|
Mod+K {
|
||||||
|
focus-window-up
|
||||||
|
}
|
||||||
|
Mod+L {
|
||||||
|
focus-column-right
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Left {
|
||||||
|
move-column-left
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Down {
|
||||||
|
move-window-down
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Up {
|
||||||
|
move-window-up
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Right {
|
||||||
|
move-column-right
|
||||||
|
}
|
||||||
|
Mod+Ctrl+H {
|
||||||
|
move-column-left
|
||||||
|
}
|
||||||
|
Mod+Ctrl+J {
|
||||||
|
move-window-down
|
||||||
|
}
|
||||||
|
Mod+Ctrl+K {
|
||||||
|
move-window-up
|
||||||
|
}
|
||||||
|
Mod+Ctrl+L {
|
||||||
|
move-column-right
|
||||||
|
}
|
||||||
|
// Mod+Shift+Down { focus-monitor-down; }
|
||||||
|
// Mod+Shift+Up { focus-monitor-up; }
|
||||||
|
// Mod+Shift+Right { focus-monitor-right; }
|
||||||
|
Mod+Shift+H {
|
||||||
|
focus-monitor-left
|
||||||
|
}
|
||||||
|
// Mod+Shift+J { focus-monitor-down; }
|
||||||
|
// Mod+Shift+K { focus-monitor-up; }
|
||||||
|
Mod+Shift+L {
|
||||||
|
focus-monitor-right
|
||||||
|
}
|
||||||
|
Mod+Shift+Ctrl+Left {
|
||||||
|
move-column-to-monitor-left
|
||||||
|
}
|
||||||
|
// Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
|
||||||
|
// Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
|
||||||
|
Mod+Shift+Ctrl+Right {
|
||||||
|
move-column-to-monitor-right
|
||||||
|
}
|
||||||
|
Mod+Shift+Ctrl+H {
|
||||||
|
move-column-to-monitor-left
|
||||||
|
}
|
||||||
|
// Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
|
||||||
|
// Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
|
||||||
|
Mod+Shift+Ctrl+L {
|
||||||
|
move-column-to-monitor-right
|
||||||
|
}
|
||||||
|
Mod+U {
|
||||||
|
focus-workspace-down
|
||||||
|
}
|
||||||
|
Mod+I {
|
||||||
|
focus-workspace-up
|
||||||
|
}
|
||||||
|
Mod+Alt+J {
|
||||||
|
move-column-to-workspace-down
|
||||||
|
}
|
||||||
|
Mod+Alt+K {
|
||||||
|
move-column-to-workspace-up
|
||||||
|
}
|
||||||
|
Mod+Alt+U {
|
||||||
|
move-column-to-workspace-down
|
||||||
|
}
|
||||||
|
Mod+Alt+I {
|
||||||
|
move-column-to-workspace-up
|
||||||
|
}
|
||||||
|
Mod+Shift+U {
|
||||||
|
move-workspace-down
|
||||||
|
}
|
||||||
|
Mod+Shift+I {
|
||||||
|
move-workspace-up
|
||||||
|
}
|
||||||
|
Mod+WheelScrollDown cooldown-ms=150 {
|
||||||
|
focus-workspace-down
|
||||||
|
}
|
||||||
|
Mod+WheelScrollUp cooldown-ms=150 {
|
||||||
|
focus-workspace-up
|
||||||
|
}
|
||||||
|
Mod+Ctrl+WheelScrollDown cooldown-ms=150 {
|
||||||
|
move-column-to-workspace-down
|
||||||
|
}
|
||||||
|
Mod+Ctrl+WheelScrollUp cooldown-ms=150 {
|
||||||
|
move-column-to-workspace-up
|
||||||
|
}
|
||||||
|
Mod+WheelScrollRight {
|
||||||
|
focus-column-right
|
||||||
|
}
|
||||||
|
Mod+WheelScrollLeft {
|
||||||
|
focus-column-left
|
||||||
|
}
|
||||||
|
Mod+Ctrl+WheelScrollRight {
|
||||||
|
move-column-right
|
||||||
|
}
|
||||||
|
Mod+Ctrl+WheelScrollLeft {
|
||||||
|
move-column-left
|
||||||
|
}
|
||||||
|
Mod+Shift+WheelScrollDown {
|
||||||
|
focus-column-right
|
||||||
|
}
|
||||||
|
Mod+Shift+WheelScrollUp {
|
||||||
|
focus-column-left
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Shift+WheelScrollDown {
|
||||||
|
move-column-right
|
||||||
|
}
|
||||||
|
Mod+Ctrl+Shift+WheelScrollUp {
|
||||||
|
move-column-left
|
||||||
|
}
|
||||||
|
Mod+1 {
|
||||||
|
focus-workspace 1
|
||||||
|
}
|
||||||
|
Mod+2 {
|
||||||
|
focus-workspace 2
|
||||||
|
}
|
||||||
|
Mod+3 {
|
||||||
|
focus-workspace 3
|
||||||
|
}
|
||||||
|
Mod+4 {
|
||||||
|
focus-workspace 4
|
||||||
|
}
|
||||||
|
Mod+5 {
|
||||||
|
focus-workspace 5
|
||||||
|
}
|
||||||
|
Mod+6 {
|
||||||
|
focus-workspace 6
|
||||||
|
}
|
||||||
|
Mod+7 {
|
||||||
|
focus-workspace 7
|
||||||
|
}
|
||||||
|
Mod+8 {
|
||||||
|
focus-workspace 8
|
||||||
|
}
|
||||||
|
Mod+9 {
|
||||||
|
focus-workspace 9
|
||||||
|
}
|
||||||
|
// Mod+Ctrl+1 { move-column-to-workspace 1; }
|
||||||
|
// Mod+Ctrl+2 { move-column-to-workspace 2; }
|
||||||
|
// Mod+Ctrl+3 { move-column-to-workspace 3; }
|
||||||
|
// Mod+Ctrl+4 { move-column-to-workspace 4; }
|
||||||
|
// Mod+Ctrl+5 { move-column-to-workspace 5; }
|
||||||
|
// Mod+Ctrl+6 { move-column-to-workspace 6; }
|
||||||
|
// Mod+Ctrl+7 { move-column-to-workspace 7; }
|
||||||
|
// Mod+Ctrl+8 { move-column-to-workspace 8; }
|
||||||
|
// Mod+Ctrl+9 { move-column-to-workspace 9; }
|
||||||
|
Mod+BracketLeft {
|
||||||
|
consume-or-expel-window-left
|
||||||
|
}
|
||||||
|
Mod+BracketRight {
|
||||||
|
consume-or-expel-window-right
|
||||||
|
}
|
||||||
|
Mod+Comma {
|
||||||
|
consume-window-into-column
|
||||||
|
}
|
||||||
|
Mod+Period {
|
||||||
|
expel-window-from-column
|
||||||
|
}
|
||||||
|
Mod+R {
|
||||||
|
switch-preset-column-width
|
||||||
|
}
|
||||||
|
// Cycling through the presets in reverse order is also possible.
|
||||||
|
// Mod+R { switch-preset-column-width-back; }
|
||||||
|
Mod+Shift+R {
|
||||||
|
switch-preset-window-height
|
||||||
|
}
|
||||||
|
Mod+Ctrl+R {
|
||||||
|
reset-window-height
|
||||||
|
}
|
||||||
|
Mod+F {
|
||||||
|
maximize-column
|
||||||
|
}
|
||||||
|
Mod+Shift+F {
|
||||||
|
fullscreen-window
|
||||||
|
}
|
||||||
|
Mod+Ctrl+F {
|
||||||
|
expand-column-to-available-width
|
||||||
|
}
|
||||||
|
// Mod+C { center-column; }
|
||||||
|
Mod+Ctrl+C {
|
||||||
|
center-visible-columns
|
||||||
|
}
|
||||||
|
Mod+Minus {
|
||||||
|
set-column-width "-10%"
|
||||||
|
}
|
||||||
|
Mod+Equal {
|
||||||
|
set-column-width "+10%"
|
||||||
|
}
|
||||||
|
// Finer height adjustments when in column with other windows.
|
||||||
|
Mod+Shift+Minus {
|
||||||
|
set-window-height "-10%"
|
||||||
|
}
|
||||||
|
Mod+Shift+Equal {
|
||||||
|
set-window-height "+10%"
|
||||||
|
}
|
||||||
|
// Move the focused window between the floating and the tiling layout.
|
||||||
|
Mod+V {
|
||||||
|
toggle-window-floating
|
||||||
|
}
|
||||||
|
Mod+Shift+V {
|
||||||
|
switch-focus-between-floating-and-tiling
|
||||||
|
}
|
||||||
|
Mod+W {
|
||||||
|
toggle-column-tabbed-display
|
||||||
|
}
|
||||||
|
// screenshot
|
||||||
|
Mod+Alt+4 {
|
||||||
|
spawn "sh" "-c" "grim -g \"$(slurp)\" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send 'Screenshot' 'Area screenshot saved'"
|
||||||
|
}
|
||||||
|
// Fullscreen and save
|
||||||
|
Mod+Alt+5 {
|
||||||
|
spawn "sh" "-c" "grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send -t 2000 'Screenshot' 'Fullscreen screenshot saved'"
|
||||||
|
}
|
||||||
|
// Select area to clipboard
|
||||||
|
Mod+Shift+4 {
|
||||||
|
spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy && notify-send -t 2000 'Screenshot' 'Area copied to clipboard'"
|
||||||
|
}
|
||||||
|
// area screenshot → annotate in swappy
|
||||||
|
Mod+Shift+5 {
|
||||||
|
spawn "sh" "-c" "grim -g \"$(slurp)\" - | swappy -f -"
|
||||||
|
}
|
||||||
|
Mod+Escape allow-inhibiting=false {
|
||||||
|
toggle-keyboard-shortcuts-inhibit
|
||||||
|
}
|
||||||
|
Mod+Shift+E {
|
||||||
|
quit
|
||||||
|
}
|
||||||
|
Ctrl+Alt+Delete {
|
||||||
|
quit
|
||||||
|
}
|
||||||
|
Mod+Shift+P {
|
||||||
|
power-off-monitors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// include "theme.kdl"
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
.base {
|
||||||
|
color: @base;
|
||||||
|
}
|
||||||
|
.surface {
|
||||||
|
color: @surface;
|
||||||
|
}
|
||||||
|
.overlay {
|
||||||
|
color: @overlay;
|
||||||
|
}
|
||||||
|
.muted {
|
||||||
|
color: @muted;
|
||||||
|
}
|
||||||
|
.subtle {
|
||||||
|
color: @subtle;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: @text;
|
||||||
|
}
|
||||||
|
.red {
|
||||||
|
color: @love;
|
||||||
|
}
|
||||||
|
.yellow {
|
||||||
|
color: @gold;
|
||||||
|
}
|
||||||
|
.green {
|
||||||
|
color: @rose;
|
||||||
|
}
|
||||||
|
.rose {
|
||||||
|
color: @pine;
|
||||||
|
}
|
||||||
|
.blue {
|
||||||
|
color: @foam;
|
||||||
|
}
|
||||||
|
.sky {
|
||||||
|
color: @iris;
|
||||||
|
}
|
||||||
|
.highlightLow {
|
||||||
|
color: @highlightLow;
|
||||||
|
}
|
||||||
|
.highlightMed {
|
||||||
|
color: @highlightMed;
|
||||||
|
}
|
||||||
|
.highlightHigh {
|
||||||
|
color: @highlightHigh;
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
"layer": "top",
|
||||||
|
"position": "top",
|
||||||
|
"height": 30,
|
||||||
|
"spacing": 0,
|
||||||
|
"modules-left": ["custom/logo", "cpu", "memory", "bluetooth"],
|
||||||
|
"modules-center": ["niri/workspaces"],
|
||||||
|
"modules-right": [
|
||||||
|
"tray",
|
||||||
|
"power-profiles-daemon",
|
||||||
|
// "niri/language",
|
||||||
|
"pulseaudio",
|
||||||
|
"network",
|
||||||
|
"battery",
|
||||||
|
"clock",
|
||||||
|
"custom/power",
|
||||||
|
],
|
||||||
|
"niri/workspaces": {
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
// Named workspaces
|
||||||
|
// (you need to configure them in niri)
|
||||||
|
// "1",
|
||||||
|
// "2": "2",
|
||||||
|
// "3": "3",
|
||||||
|
// "4": "4",
|
||||||
|
// "5": "5",
|
||||||
|
// "6": "6",
|
||||||
|
// Icons by state
|
||||||
|
"active": "",
|
||||||
|
"default": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// "mpris": {
|
||||||
|
// "format": "DEFAULT: {player_icon} {dynamic}",
|
||||||
|
// "format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
|
||||||
|
// "player-icons": {
|
||||||
|
// "default": "▶",
|
||||||
|
// "mpv": "🎵",
|
||||||
|
// },
|
||||||
|
// "status-icons": {
|
||||||
|
// "paused": "⏸",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
"tray": {
|
||||||
|
"icon-size": 14,
|
||||||
|
"spacing": 10,
|
||||||
|
},
|
||||||
|
"clock": {
|
||||||
|
"format": "{:%H:%M:%S}",
|
||||||
|
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
|
||||||
|
"format-alt": "{:%Y-%m-%d}",
|
||||||
|
"interval": 1,
|
||||||
|
},
|
||||||
|
"cpu": {
|
||||||
|
"format": "<span font='14' > </span>{usage}%",
|
||||||
|
"tooltip": false,
|
||||||
|
"interval": 1,
|
||||||
|
"on-click": "kitty --class floating_btop -e btop",
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"format": "<span font='14'> </span>{}%",
|
||||||
|
// "format": "<span font='14' class='red'> </span> {}%",
|
||||||
|
"interval": 1,
|
||||||
|
"on-click": "kitty --class floating_btop -e htop",
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"interval": 1,
|
||||||
|
"states": {
|
||||||
|
"warning": 30,
|
||||||
|
"critical": 15,
|
||||||
|
},
|
||||||
|
"format": "<span font='14' >{icon}</span> {capacity}%",
|
||||||
|
"format-full": "<span font='14' >{icon}</span> {capacity}%",
|
||||||
|
"format-charging": "<span font='14' ></span> {capacity}%",
|
||||||
|
"format-plugged": "<span font='14' ></span> {capacity}%",
|
||||||
|
"format-icons": ["", "", "", "", "", ""],
|
||||||
|
},
|
||||||
|
"power-profiles-daemon": {
|
||||||
|
"format": "<span >{icon}</span>",
|
||||||
|
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
|
||||||
|
"tooltip": true,
|
||||||
|
"format-icons": {
|
||||||
|
"default": " ",
|
||||||
|
"performance": " ",
|
||||||
|
"balanced": " ",
|
||||||
|
"power-saver": " ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"format-wifi": "<span font='14' ></span> ",
|
||||||
|
"format-ethernet": "<span font='14' ></span>",
|
||||||
|
"format-disconnected": "<span font='14' ></span>",
|
||||||
|
"format-alt": "{bandwidthDownBytes}",
|
||||||
|
"interval": 1,
|
||||||
|
// "on-click": "kitty --class floating_wavemon -e sudo wavemon",
|
||||||
|
},
|
||||||
|
"pulseaudio": {
|
||||||
|
"format": "<span font='14' >{icon}</span> {volume}%",
|
||||||
|
"format-muted": "<span font='14'></span>",
|
||||||
|
"format-source": "{volume}% ",
|
||||||
|
"format-source-muted": "",
|
||||||
|
"format-icons": {
|
||||||
|
"headphone": "",
|
||||||
|
// "hands-free": "",
|
||||||
|
// "headset": "",
|
||||||
|
// "phone": "",
|
||||||
|
// "portable": "",
|
||||||
|
// "car": "",
|
||||||
|
"default": ["", "", ""],
|
||||||
|
},
|
||||||
|
"on-click": "kitty --class floating_pulsemixer -e pulsemixer",
|
||||||
|
},
|
||||||
|
"custom/power": {
|
||||||
|
"format": "⏻",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "wlogout -l ~/.config/niri/wlogout/layout",
|
||||||
|
},
|
||||||
|
"custom/logo": {
|
||||||
|
"format": "<span font='14'></span>",
|
||||||
|
"on-click": "wofi --show drun",
|
||||||
|
},
|
||||||
|
"bluetooth": {
|
||||||
|
"format": "<span font='14'> </span>",
|
||||||
|
"format-disabled": "<span font='14'> </span>",
|
||||||
|
"format-connected": "<span font='14'> </span>",
|
||||||
|
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
|
||||||
|
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
|
||||||
|
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
||||||
|
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
|
||||||
|
"on-click": "kitty --class floating_bluetui -e bluetui",
|
||||||
|
},
|
||||||
|
// "bluetooth": {
|
||||||
|
// "format": "<span font='14'></span>",
|
||||||
|
// "format-disabled": "<span font='14'></span>",
|
||||||
|
// "format-connected": "<span font='14'></span>",
|
||||||
|
// "on-click": "kitty --class floating_bluetui -e bluetui",
|
||||||
|
// },
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
@import "theme.css";
|
||||||
|
@import "colors.css";
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: "JetBrainsMono";
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
window#waybar {
|
||||||
|
background-color: @base;
|
||||||
|
/* background: radial-gradient(alpha(@theme_base_color, 0.4), @theme_base_color); */
|
||||||
|
color: @theme_fg_color;
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
window#waybar.hidden {
|
||||||
|
/* opacity: 0.2; */
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
/* background: inherit; */
|
||||||
|
}
|
||||||
|
#workspaces {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
#workspaces button {
|
||||||
|
padding: 0 2px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: @theme_fg_color;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
#workspaces button:hover {
|
||||||
|
color: @gold;
|
||||||
|
}
|
||||||
|
#workspaces button.active,
|
||||||
|
#workspaces button.default,
|
||||||
|
#workspaces button.focused {
|
||||||
|
background: @rose;
|
||||||
|
}
|
||||||
|
#workspaces button.urgent {
|
||||||
|
background-color: @love;
|
||||||
|
}
|
||||||
|
#clock,
|
||||||
|
#battery,
|
||||||
|
#cpu,
|
||||||
|
#memory,
|
||||||
|
#disk,
|
||||||
|
#temperature,
|
||||||
|
#backlight,
|
||||||
|
#network,
|
||||||
|
#pulseaudio,
|
||||||
|
#wireplumber,
|
||||||
|
#custom-media,
|
||||||
|
#tray,
|
||||||
|
#mode,
|
||||||
|
#idle_inhibitor,
|
||||||
|
#scratchpad,
|
||||||
|
#power-profiles-daemon,
|
||||||
|
#mpd {
|
||||||
|
padding: 0 10px;
|
||||||
|
color: @theme_fg_color;
|
||||||
|
}
|
||||||
|
#window,
|
||||||
|
#workspaces {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
/* If workspaces is the leftmost module, omit left margin */
|
||||||
|
.modules-left > widget:first-child > #workspaces {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
/* If workspaces is the rightmost module, omit right margin */
|
||||||
|
.modules-right > widget:last-child > #workspaces {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
#clock {
|
||||||
|
color: @text;
|
||||||
|
}
|
||||||
|
#battery {
|
||||||
|
color: @iris;
|
||||||
|
}
|
||||||
|
#cpu {
|
||||||
|
color: @iris;
|
||||||
|
}
|
||||||
|
#memory {
|
||||||
|
color: @foam;
|
||||||
|
}
|
||||||
|
#backlight,
|
||||||
|
#network {
|
||||||
|
color: @gold;
|
||||||
|
}
|
||||||
|
#pulseaudio {
|
||||||
|
color: @rose;
|
||||||
|
}
|
||||||
|
#custom-media,
|
||||||
|
#tray,
|
||||||
|
#custom-power,
|
||||||
|
#bluetooth,
|
||||||
|
#power-profiles-daemon {
|
||||||
|
padding: 5px 15px;
|
||||||
|
color: @theme_text_color;
|
||||||
|
background-color: alpha(@theme_unfocused_bg_color, 0.4);
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
#custom-power {
|
||||||
|
background-color: @love;
|
||||||
|
border-radius: 20px 0 0 20px;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
|
#battery.charging {
|
||||||
|
color: #00ff1c;
|
||||||
|
}
|
||||||
|
#clock {
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
#pulseaudio.muted {
|
||||||
|
}
|
||||||
|
#network.disconnected {
|
||||||
|
}
|
||||||
|
#tray {
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
#custom-logo {
|
||||||
|
background-color: @pine;
|
||||||
|
padding: 0 17px;
|
||||||
|
border-radius: 0 20px 20px 0;
|
||||||
|
}
|
||||||
|
#custom-power {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#bluetooth {
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
color: @pine;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@define-color base #191724;
|
||||||
|
@define-color surface #1f1d2e;
|
||||||
|
@define-color overlay #26233a;
|
||||||
|
@define-color muted #6e6a86;
|
||||||
|
@define-color subtle #908caa;
|
||||||
|
@define-color text #e0def4;
|
||||||
|
@define-color love #eb6f92;
|
||||||
|
@define-color gold #f6c177;
|
||||||
|
@define-color rose #ea9a97;
|
||||||
|
@define-color pine #3e8fb0;
|
||||||
|
@define-color foam #9ccfd8;
|
||||||
|
@define-color iris #c4a7e7;
|
||||||
|
@define-color highlightLow #21202e;
|
||||||
|
@define-color highlightMed #403d52;
|
||||||
|
@define-color highlightHigh #524f67;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
@define-color rosewater #f5e0dc;
|
||||||
|
@define-color flamingo #f2cdcd;
|
||||||
|
@define-color pink #f5c2e7;
|
||||||
|
@define-color mauve #cba6f7;
|
||||||
|
@define-color red #f38ba8;
|
||||||
|
@define-color maroon #eba0ac;
|
||||||
|
@define-color rose #fab387;
|
||||||
|
@define-color yellow #f9e2af;
|
||||||
|
@define-color green #a6e3a1;
|
||||||
|
@define-color teal #94e2d5;
|
||||||
|
@define-color sky #89dceb;
|
||||||
|
@define-color sapphire #74c7ec;
|
||||||
|
@define-color blue #89b4fa;
|
||||||
|
@define-color lavender #b4befe;
|
||||||
|
@define-color text #cdd6f4;
|
||||||
|
@define-color subtext1 #bac2de;
|
||||||
|
@define-color subtext0 #a6adc8;
|
||||||
|
@define-color overlay2 #9399b2;
|
||||||
|
@define-color overlay1 #7f849c;
|
||||||
|
@define-color overlay #6c7086;
|
||||||
|
@define-color highlightedHigh #585b70;
|
||||||
|
@define-color highlightedMed #45475a;
|
||||||
|
@define-color highlightedlow #313244;
|
||||||
|
@define-color base #1e1e2e;
|
||||||
|
@define-color mantle #181825;
|
||||||
|
@define-color crust #11111b;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
@define-color base #232136;
|
||||||
|
@define-color surface #2a273f;
|
||||||
|
@define-color overlay #393552;
|
||||||
|
@define-color muted #6e6a86;
|
||||||
|
@define-color subtle #908caa;
|
||||||
|
@define-color text #e0def4;
|
||||||
|
@define-color red #eb6f92;
|
||||||
|
@define-color yellow #f6c177;
|
||||||
|
@define-color rose #ea9a97;
|
||||||
|
@define-color blue #3e8fb0;
|
||||||
|
@define-color sky #9ccfd8;
|
||||||
|
@define-color mauve #c4a7e7;
|
||||||
|
@define-color highlightLow #2a283e;
|
||||||
|
@define-color highlightMed #44415a;
|
||||||
|
@define-color highlightHigh #56526e;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Variant: Rosé Pine
|
||||||
|
* Maintainer: DankChoir
|
||||||
|
*/
|
||||||
|
|
||||||
|
@define-color base #191724;
|
||||||
|
@define-color surface #1f1d2e;
|
||||||
|
@define-color overlay #26233a;
|
||||||
|
|
||||||
|
@define-color muted #6e6a86;
|
||||||
|
@define-color subtle #908caa;
|
||||||
|
@define-color text #e0def4;
|
||||||
|
|
||||||
|
@define-color love #eb6f92;
|
||||||
|
@define-color gold #f6c177;
|
||||||
|
@define-color rose #ebbcba;
|
||||||
|
@define-color pine #31748f;
|
||||||
|
@define-color foam #9ccfd8;
|
||||||
|
@define-color iris #c4a7e7;
|
||||||
|
|
||||||
|
@define-color highlightLow #21202e;
|
||||||
|
@define-color highlightMed #403d52;
|
||||||
|
@define-color highlightHigh #524f67;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"label": "lock",
|
||||||
|
"action": "hyprlock",
|
||||||
|
"text": "Lock",
|
||||||
|
"keybind": "l"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"label": "logout",
|
||||||
|
"action": "niri msg action quit",
|
||||||
|
"text": "Logout",
|
||||||
|
"keybind": "e"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"label": "suspend",
|
||||||
|
"action": "systemctl suspend",
|
||||||
|
"text": "Suspend",
|
||||||
|
"keybind": "u"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"label": "hibernate",
|
||||||
|
"action": "systemctl hibernate",
|
||||||
|
"text": "Hibernate",
|
||||||
|
"keybind": "h"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"label": "shutdown",
|
||||||
|
"action": "systemctl poweroff",
|
||||||
|
"text": "Shutdown",
|
||||||
|
"keybind": "s"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"label": "reboot",
|
||||||
|
"action": "systemctl reboot",
|
||||||
|
"text": "Reboot",
|
||||||
|
"keybind": "r"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
alias cl = clear
|
||||||
|
alias y = yazi
|
||||||
|
alias neo = neofetch
|
||||||
|
alias a = aerc
|
||||||
|
alias oc = opencode
|
||||||
|
|
||||||
|
# nvim
|
||||||
|
alias n = nvim
|
||||||
|
alias nv = nvim
|
||||||
|
alias nano = nvim
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
def --env cld [path: path = "~"] { clear; cd $path }
|
||||||
@@ -0,0 +1,954 @@
|
|||||||
|
# Nushell Config File
|
||||||
|
#
|
||||||
|
# version = "0.95.0"
|
||||||
|
|
||||||
|
# For more information on defining custom themes, see
|
||||||
|
# https://www.nushell.sh/book/coloring_and_theming.html
|
||||||
|
# And here is the theme collection
|
||||||
|
# https://github.com/nushell/nu_scripts/tree/main/themes
|
||||||
|
let dark_theme = {
|
||||||
|
# color for nushell primitives
|
||||||
|
separator: white
|
||||||
|
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
||||||
|
header: green_bold
|
||||||
|
empty: blue
|
||||||
|
# Closures can be used to choose colors for specific values.
|
||||||
|
# The value (in this case, a bool) is piped into the closure.
|
||||||
|
# eg) {|| if $in { 'light_cyan' } else { 'light_gray' } }
|
||||||
|
bool: light_cyan
|
||||||
|
int: white
|
||||||
|
filesize: cyan
|
||||||
|
duration: white
|
||||||
|
date: purple
|
||||||
|
range: white
|
||||||
|
float: white
|
||||||
|
string: white
|
||||||
|
nothing: white
|
||||||
|
binary: white
|
||||||
|
cell-path: white
|
||||||
|
row_index: green_bold
|
||||||
|
record: white
|
||||||
|
list: white
|
||||||
|
block: white
|
||||||
|
hints: dark_gray
|
||||||
|
search_result: { bg: red fg: white }
|
||||||
|
shape_and: purple_bold
|
||||||
|
shape_binary: purple_bold
|
||||||
|
shape_block: blue_bold
|
||||||
|
shape_bool: light_cyan
|
||||||
|
shape_closure: green_bold
|
||||||
|
shape_custom: green
|
||||||
|
shape_datetime: cyan_bold
|
||||||
|
shape_directory: cyan
|
||||||
|
shape_external: cyan
|
||||||
|
shape_externalarg: green_bold
|
||||||
|
shape_external_resolved: light_yellow_bold
|
||||||
|
shape_filepath: cyan
|
||||||
|
shape_flag: blue_bold
|
||||||
|
shape_float: purple_bold
|
||||||
|
# shapes are used to change the cli syntax highlighting
|
||||||
|
shape_garbage: { fg: white bg: red attr: b}
|
||||||
|
shape_glob_interpolation: cyan_bold
|
||||||
|
shape_globpattern: cyan_bold
|
||||||
|
shape_int: purple_bold
|
||||||
|
shape_internalcall: cyan_bold
|
||||||
|
shape_keyword: cyan_bold
|
||||||
|
shape_list: cyan_bold
|
||||||
|
shape_literal: blue
|
||||||
|
shape_match_pattern: green
|
||||||
|
shape_matching_brackets: { attr: u }
|
||||||
|
shape_nothing: light_cyan
|
||||||
|
shape_operator: yellow
|
||||||
|
shape_or: purple_bold
|
||||||
|
shape_pipe: purple_bold
|
||||||
|
shape_range: yellow_bold
|
||||||
|
shape_record: cyan_bold
|
||||||
|
shape_redirection: purple_bold
|
||||||
|
shape_signature: green_bold
|
||||||
|
shape_string: green
|
||||||
|
shape_string_interpolation: cyan_bold
|
||||||
|
shape_table: blue_bold
|
||||||
|
shape_variable: purple
|
||||||
|
shape_vardecl: purple
|
||||||
|
shape_raw_string: light_purple
|
||||||
|
}
|
||||||
|
|
||||||
|
let light_theme = {
|
||||||
|
# color for nushell primitives
|
||||||
|
separator: dark_gray
|
||||||
|
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
||||||
|
header: green_bold
|
||||||
|
empty: blue
|
||||||
|
# Closures can be used to choose colors for specific values.
|
||||||
|
# The value (in this case, a bool) is piped into the closure.
|
||||||
|
# eg) {|| if $in { 'dark_cyan' } else { 'dark_gray' } }
|
||||||
|
bool: dark_cyan
|
||||||
|
int: dark_gray
|
||||||
|
filesize: cyan_bold
|
||||||
|
duration: dark_gray
|
||||||
|
date: purple
|
||||||
|
range: dark_gray
|
||||||
|
float: dark_gray
|
||||||
|
string: dark_gray
|
||||||
|
nothing: dark_gray
|
||||||
|
binary: dark_gray
|
||||||
|
cell-path: dark_gray
|
||||||
|
row_index: green_bold
|
||||||
|
record: dark_gray
|
||||||
|
list: dark_gray
|
||||||
|
block: dark_gray
|
||||||
|
hints: dark_gray
|
||||||
|
search_result: { fg: white bg: red }
|
||||||
|
shape_and: purple_bold
|
||||||
|
shape_binary: purple_bold
|
||||||
|
shape_block: blue_bold
|
||||||
|
shape_bool: light_cyan
|
||||||
|
shape_closure: green_bold
|
||||||
|
shape_custom: green
|
||||||
|
shape_datetime: cyan_bold
|
||||||
|
shape_directory: cyan
|
||||||
|
shape_external: cyan
|
||||||
|
shape_externalarg: green_bold
|
||||||
|
shape_external_resolved: light_purple_bold
|
||||||
|
shape_filepath: cyan
|
||||||
|
shape_flag: blue_bold
|
||||||
|
shape_float: purple_bold
|
||||||
|
# shapes are used to change the cli syntax highlighting
|
||||||
|
shape_garbage: { fg: white bg: red attr: b}
|
||||||
|
shape_globpattern: cyan_bold
|
||||||
|
shape_int: purple_bold
|
||||||
|
shape_internalcall: cyan_bold
|
||||||
|
shape_keyword: cyan_bold
|
||||||
|
shape_list: cyan_bold
|
||||||
|
shape_literal: blue
|
||||||
|
shape_match_pattern: green
|
||||||
|
shape_matching_brackets: { attr: u }
|
||||||
|
shape_nothing: light_cyan
|
||||||
|
shape_operator: yellow
|
||||||
|
shape_or: purple_bold
|
||||||
|
shape_pipe: purple_bold
|
||||||
|
shape_range: yellow_bold
|
||||||
|
shape_record: cyan_bold
|
||||||
|
shape_redirection: purple_bold
|
||||||
|
shape_signature: green_bold
|
||||||
|
shape_string: green
|
||||||
|
shape_string_interpolation: cyan_bold
|
||||||
|
shape_table: blue_bold
|
||||||
|
shape_variable: purple
|
||||||
|
shape_vardecl: purple
|
||||||
|
shape_raw_string: light_purple
|
||||||
|
}
|
||||||
|
|
||||||
|
$env.config = {
|
||||||
|
show_banner: false
|
||||||
|
|
||||||
|
ls: {
|
||||||
|
use_ls_colors: true
|
||||||
|
}
|
||||||
|
|
||||||
|
rm: {
|
||||||
|
always_trash: false # always act as if -t was given. Can be overridden with -p
|
||||||
|
}
|
||||||
|
|
||||||
|
table: {
|
||||||
|
mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other
|
||||||
|
index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column
|
||||||
|
show_empty: true # show 'empty list' and 'empty record' placeholders for command output
|
||||||
|
padding: { left: 1, right: 1 } # a left right padding of each column in a table
|
||||||
|
trim: {
|
||||||
|
methodology: wrapping # wrapping or truncating
|
||||||
|
wrapping_try_keep_words: true # A strategy used by the 'wrapping' methodology
|
||||||
|
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
|
||||||
|
}
|
||||||
|
header_on_separator: false # show header text on separator/border line
|
||||||
|
# abbreviated_row_count: 10 # limit data rows from top and bottom after reaching a set point
|
||||||
|
}
|
||||||
|
|
||||||
|
error_style: "fancy" # "fancy" or "plain" for screen reader-friendly error messages
|
||||||
|
|
||||||
|
# datetime_format determines what a datetime rendered in the shell would look like.
|
||||||
|
# Behavior without this configuration point will be to "humanize" the datetime display,
|
||||||
|
# showing something like "a day ago."
|
||||||
|
datetime_format: {
|
||||||
|
# normal: '%a, %d %b %Y %H:%M:%S %z' # shows up in displays of variables or other datetime's outside of tables
|
||||||
|
# table: '%m/%d/%y %I:%M:%S%p' # generally shows up in tabular outputs such as ls. commenting this out will change it to the default human readable datetime format
|
||||||
|
}
|
||||||
|
|
||||||
|
explore: {
|
||||||
|
status_bar_background: { fg: "#1D1F21", bg: "#C4C9C6" },
|
||||||
|
command_bar_text: { fg: "#C4C9C6" },
|
||||||
|
highlight: { fg: "black", bg: "yellow" },
|
||||||
|
status: {
|
||||||
|
error: { fg: "white", bg: "red" },
|
||||||
|
warn: {}
|
||||||
|
info: {}
|
||||||
|
},
|
||||||
|
selected_cell: { bg: light_blue },
|
||||||
|
}
|
||||||
|
|
||||||
|
history: {
|
||||||
|
max_size: 100_000 # Session has to be reloaded for this to take effect
|
||||||
|
sync_on_enter: true # Enable to share history between multiple sessions, else you have to close the session to write history to file
|
||||||
|
file_format: "plaintext" # "sqlite" or "plaintext"
|
||||||
|
isolation: false # only available with sqlite file_format. true enables history isolation, false disables it. true will allow the history to be isolated to the current session using up/down arrows. false will allow the history to be shared across all sessions.
|
||||||
|
}
|
||||||
|
|
||||||
|
completions: {
|
||||||
|
case_sensitive: false # set to true to enable case-sensitive completions
|
||||||
|
quick: true # set this to false to prevent auto-selecting completions when only one remains
|
||||||
|
partial: true # set this to false to prevent partial filling of the prompt
|
||||||
|
algorithm: "prefix" # prefix or fuzzy
|
||||||
|
external: {
|
||||||
|
enable: true # set to false to prevent nushell looking into $env.PATH to find more suggestions, `false` recommended for WSL users as this look up may be very slow
|
||||||
|
max_results: 100 # setting it lower can improve completion performance at the cost of omitting some options
|
||||||
|
completer: null # check 'carapace_completer' above as an example
|
||||||
|
}
|
||||||
|
use_ls_colors: true # set this to true to enable file/path/directory completions using LS_COLORS
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor_shape: {
|
||||||
|
emacs: block # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (line is the default)
|
||||||
|
vi_insert: block # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (block is the default)
|
||||||
|
vi_normal: underscore # block, underscore, line, blink_block, blink_underscore, blink_line, inherit to skip setting cursor shape (underscore is the default)
|
||||||
|
}
|
||||||
|
|
||||||
|
color_config: $dark_theme # if you want a more interesting theme, you can replace the empty record with `$dark_theme`, `$light_theme` or another custom record
|
||||||
|
footer_mode: 25 # always, never, number_of_rows, auto
|
||||||
|
float_precision: 2 # the precision for displaying floats in tables
|
||||||
|
buffer_editor: "" # command that will be used to edit the current line buffer with ctrl+o, if unset fallback to $env.EDITOR and $env.VISUAL
|
||||||
|
use_ansi_coloring: true
|
||||||
|
bracketed_paste: true # enable bracketed paste, currently useless on windows
|
||||||
|
edit_mode: vi # emacs, vi
|
||||||
|
shell_integration: {
|
||||||
|
# osc2 abbreviates the path if in the home_dir, sets the tab/window title, shows the running command in the tab/window title
|
||||||
|
osc2: true
|
||||||
|
# osc7 is a way to communicate the path to the terminal, this is helpful for spawning new tabs in the same directory
|
||||||
|
osc7: true
|
||||||
|
# osc8 is also implemented as the deprecated setting ls.show_clickable_links, it shows clickable links in ls output if your terminal supports it. show_clickable_links is deprecated in favor of osc8
|
||||||
|
osc8: true
|
||||||
|
# osc9_9 is from ConEmu and is starting to get wider support. It's similar to osc7 in that it communicates the path to the terminal
|
||||||
|
osc9_9: false
|
||||||
|
# osc133 is several escapes invented by Final Term which include the supported ones below.
|
||||||
|
# 133;A - Mark prompt start
|
||||||
|
# 133;B - Mark prompt end
|
||||||
|
# 133;C - Mark pre-execution
|
||||||
|
# 133;D;exit - Mark execution finished with exit code
|
||||||
|
# This is used to enable terminals to know where the prompt is, the command is, where the command finishes, and where the output of the command is
|
||||||
|
osc133: true
|
||||||
|
# osc633 is closely related to osc133 but only exists in visual studio code (vscode) and supports their shell integration features
|
||||||
|
# 633;A - Mark prompt start
|
||||||
|
# 633;B - Mark prompt end
|
||||||
|
# 633;C - Mark pre-execution
|
||||||
|
# 633;D;exit - Mark execution finished with exit code
|
||||||
|
# 633;E - NOT IMPLEMENTED - Explicitly set the command line with an optional nonce
|
||||||
|
# 633;P;Cwd=<path> - Mark the current working directory and communicate it to the terminal
|
||||||
|
# and also helps with the run recent menu in vscode
|
||||||
|
osc633: true
|
||||||
|
# reset_application_mode is escape \x1b[?1l and was added to help ssh work better
|
||||||
|
reset_application_mode: true
|
||||||
|
}
|
||||||
|
render_right_prompt_on_last_line: false # true or false to enable or disable right prompt to be rendered on last line of the prompt.
|
||||||
|
use_kitty_protocol: false # enables keyboard enhancement protocol implemented by kitty console, only if your terminal support this.
|
||||||
|
highlight_resolved_externals: false # true enables highlighting of external commands in the repl resolved by which.
|
||||||
|
recursion_limit: 50 # the maximum number of times nushell allows recursion before stopping it
|
||||||
|
|
||||||
|
plugins: {} # Per-plugin configuration. See https://www.nushell.sh/contributor-book/plugins.html#configuration.
|
||||||
|
|
||||||
|
plugin_gc: {
|
||||||
|
# Configuration for plugin garbage collection
|
||||||
|
default: {
|
||||||
|
enabled: true # true to enable stopping of inactive plugins
|
||||||
|
stop_after: 10sec # how long to wait after a plugin is inactive to stop it
|
||||||
|
}
|
||||||
|
plugins: {
|
||||||
|
# alternate configuration for specific plugins, by name, for example:
|
||||||
|
#
|
||||||
|
# gstat: {
|
||||||
|
# enabled: false
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks: {
|
||||||
|
pre_prompt: [{||
|
||||||
|
if (which direnv | is-empty) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
direnv export json | from json | default {} | load-env
|
||||||
|
if 'PATH' in $env {
|
||||||
|
$env.PATH = ($env.PATH | split row (char esep))
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}]
|
||||||
|
pre_execution: [{ null }] # run before the repl input is run
|
||||||
|
env_change: {
|
||||||
|
PWD: [] # run if the PWD environment is different since the last repl input
|
||||||
|
}
|
||||||
|
display_output: "if (term size).columns >= 100 { table -e } else { table }" # run to display the output of a pipeline
|
||||||
|
command_not_found: { null } # return an error message when a command is not found
|
||||||
|
}
|
||||||
|
|
||||||
|
menus: [
|
||||||
|
# Configuration for default nushell menus
|
||||||
|
# Note the lack of source parameter
|
||||||
|
{
|
||||||
|
name: completion_menu
|
||||||
|
only_buffer_difference: false
|
||||||
|
marker: "| "
|
||||||
|
type: {
|
||||||
|
layout: columnar
|
||||||
|
columns: 4
|
||||||
|
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
|
||||||
|
col_padding: 2
|
||||||
|
}
|
||||||
|
style: {
|
||||||
|
text: green
|
||||||
|
selected_text: { attr: r }
|
||||||
|
description_text: yellow
|
||||||
|
match_text: { attr: u }
|
||||||
|
selected_match_text: { attr: ur }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: ide_completion_menu
|
||||||
|
only_buffer_difference: false
|
||||||
|
marker: "| "
|
||||||
|
type: {
|
||||||
|
layout: ide
|
||||||
|
min_completion_width: 0,
|
||||||
|
max_completion_width: 50,
|
||||||
|
max_completion_height: 10, # will be limited by the available lines in the terminal
|
||||||
|
padding: 0,
|
||||||
|
border: true,
|
||||||
|
cursor_offset: 0,
|
||||||
|
description_mode: "prefer_right"
|
||||||
|
min_description_width: 0
|
||||||
|
max_description_width: 50
|
||||||
|
max_description_height: 10
|
||||||
|
description_offset: 1
|
||||||
|
# If true, the cursor pos will be corrected, so the suggestions match up with the typed text
|
||||||
|
#
|
||||||
|
# C:\> str
|
||||||
|
# str join
|
||||||
|
# str trim
|
||||||
|
# str split
|
||||||
|
correct_cursor_pos: false
|
||||||
|
}
|
||||||
|
style: {
|
||||||
|
text: green
|
||||||
|
selected_text: { attr: r }
|
||||||
|
description_text: yellow
|
||||||
|
match_text: { attr: u }
|
||||||
|
selected_match_text: { attr: ur }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: history_menu
|
||||||
|
only_buffer_difference: true
|
||||||
|
marker: "? "
|
||||||
|
type: {
|
||||||
|
layout: list
|
||||||
|
page_size: 10
|
||||||
|
}
|
||||||
|
style: {
|
||||||
|
text: green
|
||||||
|
selected_text: green_reverse
|
||||||
|
description_text: yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: help_menu
|
||||||
|
only_buffer_difference: true
|
||||||
|
marker: "? "
|
||||||
|
type: {
|
||||||
|
layout: description
|
||||||
|
columns: 4
|
||||||
|
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
|
||||||
|
col_padding: 2
|
||||||
|
selection_rows: 4
|
||||||
|
description_rows: 10
|
||||||
|
}
|
||||||
|
style: {
|
||||||
|
text: green
|
||||||
|
selected_text: green_reverse
|
||||||
|
description_text: yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
keybindings: [
|
||||||
|
{
|
||||||
|
name: delete_one_word_backward
|
||||||
|
modifier: alt
|
||||||
|
keycode: backspace
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {edit: backspaceword}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: completion_menu
|
||||||
|
modifier: none
|
||||||
|
keycode: tab
|
||||||
|
mode: [emacs vi_normal vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menu name: completion_menu }
|
||||||
|
{ send: menunext }
|
||||||
|
{ edit: complete }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: ide_completion_menu
|
||||||
|
modifier: control
|
||||||
|
keycode: char_n
|
||||||
|
mode: [emacs vi_normal vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menu name: ide_completion_menu }
|
||||||
|
{ send: menunext }
|
||||||
|
{ edit: complete }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: history_menu
|
||||||
|
modifier: control
|
||||||
|
keycode: char_r
|
||||||
|
mode: [emacs, vi_insert, vi_normal]
|
||||||
|
event: { send: menu name: history_menu }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: help_menu
|
||||||
|
modifier: none
|
||||||
|
keycode: f1
|
||||||
|
mode: [emacs, vi_insert, vi_normal]
|
||||||
|
event: { send: menu name: help_menu }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: completion_previous_menu
|
||||||
|
modifier: shift
|
||||||
|
keycode: backtab
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: menuprevious }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: next_page_menu
|
||||||
|
modifier: control
|
||||||
|
keycode: char_x
|
||||||
|
mode: emacs
|
||||||
|
event: { send: menupagenext }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: undo_or_previous_page_menu
|
||||||
|
modifier: control
|
||||||
|
keycode: char_z
|
||||||
|
mode: emacs
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menupageprevious }
|
||||||
|
{ edit: undo }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: escape
|
||||||
|
modifier: none
|
||||||
|
keycode: escape
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: esc } # NOTE: does not appear to work
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cancel_command
|
||||||
|
modifier: control
|
||||||
|
keycode: char_c
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: ctrlc }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: quit_shell
|
||||||
|
modifier: control
|
||||||
|
keycode: char_d
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: ctrld }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: clear_screen
|
||||||
|
modifier: control
|
||||||
|
keycode: char_l
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: clearscreen }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: search_history
|
||||||
|
modifier: control
|
||||||
|
keycode: char_q
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: searchhistory }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: open_command_editor
|
||||||
|
modifier: control
|
||||||
|
keycode: char_o
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { send: openeditor }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_up
|
||||||
|
modifier: none
|
||||||
|
keycode: up
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menuup }
|
||||||
|
{ send: up }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_down
|
||||||
|
modifier: none
|
||||||
|
keycode: down
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menudown }
|
||||||
|
{ send: down }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_left
|
||||||
|
modifier: none
|
||||||
|
keycode: left
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menuleft }
|
||||||
|
{ send: left }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_right_or_take_history_hint
|
||||||
|
modifier: none
|
||||||
|
keycode: right
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintcomplete }
|
||||||
|
{ send: menuright }
|
||||||
|
{ send: right }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_left
|
||||||
|
modifier: control
|
||||||
|
keycode: left
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { edit: movewordleft }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_right_or_take_history_hint
|
||||||
|
modifier: control
|
||||||
|
keycode: right
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintwordcomplete }
|
||||||
|
{ edit: movewordright }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_start
|
||||||
|
modifier: none
|
||||||
|
keycode: home
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { edit: movetolinestart }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_start
|
||||||
|
modifier: control
|
||||||
|
keycode: char_a
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { edit: movetolinestart }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_end_or_take_history_hint
|
||||||
|
modifier: none
|
||||||
|
keycode: end
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintcomplete }
|
||||||
|
{ edit: movetolineend }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_end_or_take_history_hint
|
||||||
|
modifier: control
|
||||||
|
keycode: char_e
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintcomplete }
|
||||||
|
{ edit: movetolineend }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_start
|
||||||
|
modifier: control
|
||||||
|
keycode: home
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { edit: movetolinestart }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_to_line_end
|
||||||
|
modifier: control
|
||||||
|
keycode: end
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: { edit: movetolineend }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_up
|
||||||
|
modifier: control
|
||||||
|
keycode: char_p
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menuup }
|
||||||
|
{ send: up }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_down
|
||||||
|
modifier: control
|
||||||
|
keycode: char_t
|
||||||
|
mode: [emacs, vi_normal, vi_insert]
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menudown }
|
||||||
|
{ send: down }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_character_backward
|
||||||
|
modifier: none
|
||||||
|
keycode: backspace
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: backspace }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_word_backward
|
||||||
|
modifier: control
|
||||||
|
keycode: backspace
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: backspaceword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_character_forward
|
||||||
|
modifier: none
|
||||||
|
keycode: delete
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: delete }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_character_forward
|
||||||
|
modifier: control
|
||||||
|
keycode: delete
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: delete }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_character_backward
|
||||||
|
modifier: control
|
||||||
|
keycode: char_h
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: backspace }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_word_backward
|
||||||
|
modifier: control
|
||||||
|
keycode: char_w
|
||||||
|
mode: [emacs, vi_insert]
|
||||||
|
event: { edit: backspaceword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_left
|
||||||
|
modifier: none
|
||||||
|
keycode: backspace
|
||||||
|
mode: vi_normal
|
||||||
|
event: { edit: moveleft }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: newline_or_run_command
|
||||||
|
modifier: none
|
||||||
|
keycode: enter
|
||||||
|
mode: emacs
|
||||||
|
event: { send: enter }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_left
|
||||||
|
modifier: control
|
||||||
|
keycode: char_b
|
||||||
|
mode: emacs
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: menuleft }
|
||||||
|
{ send: left }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_right_or_take_history_hint
|
||||||
|
modifier: control
|
||||||
|
keycode: char_f
|
||||||
|
mode: emacs
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintcomplete }
|
||||||
|
{ send: menuright }
|
||||||
|
{ send: right }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: redo_change
|
||||||
|
modifier: control
|
||||||
|
keycode: char_g
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: redo }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: undo_change
|
||||||
|
modifier: control
|
||||||
|
keycode: char_z
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: undo }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: paste_before
|
||||||
|
modifier: control
|
||||||
|
keycode: char_y
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: pastecutbufferbefore }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cut_word_left
|
||||||
|
modifier: control
|
||||||
|
keycode: char_w
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: cutwordleft }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cut_line_to_end
|
||||||
|
modifier: control
|
||||||
|
keycode: char_k
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: cuttoend }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cut_line_from_start
|
||||||
|
modifier: control
|
||||||
|
keycode: char_u
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: cutfromstart }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: swap_graphemes
|
||||||
|
modifier: control
|
||||||
|
keycode: char_t
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: swapgraphemes }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_left
|
||||||
|
modifier: alt
|
||||||
|
keycode: left
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: movewordleft }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_right_or_take_history_hint
|
||||||
|
modifier: alt
|
||||||
|
keycode: right
|
||||||
|
mode: emacs
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintwordcomplete }
|
||||||
|
{ edit: movewordright }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_left
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_b
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: movewordleft }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: move_one_word_right_or_take_history_hint
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_f
|
||||||
|
mode: emacs
|
||||||
|
event: {
|
||||||
|
until: [
|
||||||
|
{ send: historyhintwordcomplete }
|
||||||
|
{ edit: movewordright }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_word_forward
|
||||||
|
modifier: alt
|
||||||
|
keycode: delete
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: deleteword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_word_backward
|
||||||
|
modifier: alt
|
||||||
|
keycode: backspace
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: backspaceword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: delete_one_word_backward
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_m
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: backspaceword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cut_word_to_right
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_d
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: cutwordright }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: upper_case_word
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_u
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: uppercaseword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: lower_case_word
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_l
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: lowercaseword }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: capitalize_char
|
||||||
|
modifier: alt
|
||||||
|
keycode: char_c
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: capitalizechar }
|
||||||
|
}
|
||||||
|
# The following bindings with `*system` events require that Nushell has
|
||||||
|
# been compiled with the `system-clipboard` feature.
|
||||||
|
# This should be the case for Windows, macOS, and most Linux distributions
|
||||||
|
# Not available for example on Android (termux)
|
||||||
|
# If you want to use the system clipboard for visual selection or to
|
||||||
|
# paste directly, uncomment the respective lines and replace the version
|
||||||
|
# using the internal clipboard.
|
||||||
|
{
|
||||||
|
name: copy_selection
|
||||||
|
modifier: control_shift
|
||||||
|
keycode: char_c
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: copyselection }
|
||||||
|
# event: { edit: copyselectionsystem }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: cut_selection
|
||||||
|
modifier: control_shift
|
||||||
|
keycode: char_x
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: cutselection }
|
||||||
|
# event: { edit: cutselectionsystem }
|
||||||
|
}
|
||||||
|
# {
|
||||||
|
# name: paste_system
|
||||||
|
# modifier: control_shift
|
||||||
|
# keycode: char_v
|
||||||
|
# mode: emacs
|
||||||
|
# event: { edit: pastesystem }
|
||||||
|
# }
|
||||||
|
{
|
||||||
|
name: select_all
|
||||||
|
modifier: control_shift
|
||||||
|
keycode: char_a
|
||||||
|
mode: emacs
|
||||||
|
event: { edit: selectall }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def --env cx [arg] {
|
||||||
|
cd $arg
|
||||||
|
ls -l
|
||||||
|
}
|
||||||
|
|
||||||
|
alias l = ls --all
|
||||||
|
alias c = clear
|
||||||
|
alias ll = ls -l
|
||||||
|
alias lt = eza --tree --level=2 --long --icons --git
|
||||||
|
alias v = nvim
|
||||||
|
alias as = aerospace
|
||||||
|
alias asr = atuin scripts run
|
||||||
|
|
||||||
|
def ff [] {
|
||||||
|
aerospace list-windows --all | fzf --bind 'enter:execute(bash -c "aerospace focus --window-id {1}")+abort'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Git
|
||||||
|
alias gc = git commit -m
|
||||||
|
alias gca = git commit -a -m
|
||||||
|
alias gp = git push origin HEAD
|
||||||
|
alias gpu = git pull origin
|
||||||
|
alias gst = git status
|
||||||
|
alias glog = git log --graph --topo-order --pretty='%w(100,0,6)%C(yellow)%h%C(bold)%C(black)%d %C(cyan)%ar %C(green)%an%n%C(bold)%C(white)%s %N' --abbrev-commit
|
||||||
|
alias gdiff = git diff
|
||||||
|
alias gco = git checkout
|
||||||
|
alias gb = git branch
|
||||||
|
alias gba = git branch -a
|
||||||
|
alias gadd = git add
|
||||||
|
alias ga = git add -p
|
||||||
|
alias gcoall = git checkout -- .
|
||||||
|
alias gr = git remote
|
||||||
|
alias gre = git reset
|
||||||
|
|
||||||
|
# K8s
|
||||||
|
alias k = kubectl
|
||||||
|
alias ka = kubectl apply -f
|
||||||
|
alias kg = kubectl get
|
||||||
|
alias kd = kubectl describe
|
||||||
|
alias kdel = kubectl delete
|
||||||
|
alias kl = kubectl logs
|
||||||
|
alias kgpo = kubectl get pod
|
||||||
|
alias kgd = kubectl get deployments
|
||||||
|
alias kc = kubectx
|
||||||
|
alias kns = kubens
|
||||||
|
alias kl = kubectl logs -f
|
||||||
|
alias ke = kubectl exec -it
|
||||||
|
|
||||||
|
# source ~/.zoxide.nu
|
||||||
|
source ~/.cache/carapace/init.nu
|
||||||
|
source ~/.local/share/atuin/init.nu
|
||||||
|
use ~/.cache/starship/init.nu
|
||||||
|
use ~/.cache/mise/init.nu
|
||||||
|
|
||||||
|
|
||||||
|
$env.DIRENV_LOG_FORMAT = ""
|
||||||
|
|
||||||
|
source ~/.config/nushell/vendor/autoload/wt.nu
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# config.nu
|
# config.nu
|
||||||
#
|
def --env cld [path: path = "~"] { clear; cd $path }
|
||||||
# Installed by:
|
# Installed by:
|
||||||
# version = "0.103.0"
|
# version = "0.103.0"
|
||||||
#
|
#
|
||||||
@@ -151,28 +151,9 @@ let light_theme = {
|
|||||||
shape_raw_string: light_purple
|
shape_raw_string: light_purple
|
||||||
}
|
}
|
||||||
|
|
||||||
# Example prompt with Nerd Font icons
|
|
||||||
def create_left_prompt [] {
|
|
||||||
let dir = ([
|
|
||||||
(char -u "e0b1"),
|
|
||||||
($env.PWD | str replace $nu.home-path "~")
|
|
||||||
] | str join)
|
|
||||||
|
|
||||||
let git_branch = (do -i { git rev-parse --abbrev-ref HEAD } | default "")
|
|
||||||
|
|
||||||
$"(ansi green)($dir)(ansi reset) (ansi purple) ($git_branch)(ansi reset)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$env.PROMPT_COMMAND = { create_left_prompt }
|
|
||||||
|
|
||||||
$env.GIT_DISCOVERY_ACROSS_FILESYSTEM = "1" # Allows Git to check parent dirs
|
|
||||||
|
|
||||||
def --wrapped git [...args] {
|
|
||||||
^git ...$args err> /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
## Alias
|
## Alias
|
||||||
source ~/.alias.nu
|
source ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/.calias.nu
|
||||||
|
|
||||||
### autocompletion
|
### autocompletion
|
||||||
## zoxide
|
## zoxide
|
||||||
|
|||||||
@@ -361,3 +361,156 @@ cld
|
|||||||
exit
|
exit
|
||||||
bat ~/.ssh/id_ed25519.pub
|
bat ~/.ssh/id_ed25519.pub
|
||||||
exit
|
exit
|
||||||
|
ls ~/.config
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
cd ~/.config/nushell
|
||||||
|
ls
|
||||||
|
nvim config.nu
|
||||||
|
touch config-om.nu
|
||||||
|
nvim config-om.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
ls
|
||||||
|
q
|
||||||
|
cd ~/.config/nushell
|
||||||
|
touch om.env
|
||||||
|
nvim om.env
|
||||||
|
exit
|
||||||
|
cd ~/.config/nushell
|
||||||
|
nvim config-om.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
exit
|
||||||
|
nvim ~/.config/nushell/config-om.nu
|
||||||
|
nvim ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
touch .alias.nu
|
||||||
|
nvim ~/.config/nushell/config.nu
|
||||||
|
nvim .alias.nu
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
y
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
nvim ~/.config/nushell/config-om.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
gcc
|
||||||
|
n
|
||||||
|
nvim ~/.config/nushell/config.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
n
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
nvim .alias.nu
|
||||||
|
y
|
||||||
|
yazi
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
nv
|
||||||
|
mv
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/config.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
exit
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/config.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
n
|
||||||
|
ls dotfiles/
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cld
|
||||||
|
cd dotfiles/
|
||||||
|
cld
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
cld
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
exit
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
cd
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
nvim ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
exit
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cd
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
y
|
||||||
|
eix
|
||||||
|
exit
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
y
|
||||||
|
exit
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
cd
|
||||||
|
cd ~/.config/nushell
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/config.nu
|
||||||
|
cd ~/.config/nushell
|
||||||
|
cld
|
||||||
|
y
|
||||||
|
source ~/.config/nushell/.alias.nu
|
||||||
|
n ~/.config/nushell/.alias.nu
|
||||||
|
source ~/.config/nushell/.alias.nu
|
||||||
|
n
|
||||||
|
cld
|
||||||
|
exit
|
||||||
|
ls dotfiles
|
||||||
|
cld
|
||||||
|
ls -la ~/dotfiles/
|
||||||
|
ls ~/dotfiles/
|
||||||
|
exit
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#
|
||||||
|
# Nushell Environment Config File
|
||||||
|
#
|
||||||
|
# version = "0.95.0"
|
||||||
|
|
||||||
|
def create_left_prompt [] {
|
||||||
|
let dir = match (do -i { $env.PWD | path relative-to $nu.home-path }) {
|
||||||
|
null => $env.PWD
|
||||||
|
'' => '~'
|
||||||
|
$relative_pwd => ([~ $relative_pwd] | path join)
|
||||||
|
}
|
||||||
|
|
||||||
|
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
|
||||||
|
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
|
||||||
|
let path_segment = $"($path_color)($dir)"
|
||||||
|
|
||||||
|
$path_segment | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_right_prompt [] {
|
||||||
|
# create a right prompt in magenta with green separators and am/pm underlined
|
||||||
|
let time_segment = ([
|
||||||
|
(ansi reset)
|
||||||
|
(ansi magenta)
|
||||||
|
(date now | format date '%x %X') # try to respect user's locale
|
||||||
|
] | str join | str replace --regex --all "([/:])" $"(ansi green)${1}(ansi magenta)" |
|
||||||
|
str replace --regex --all "([AP]M)" $"(ansi magenta_underline)${1}")
|
||||||
|
|
||||||
|
let last_exit_code = if ($env.LAST_EXIT_CODE != 0) {([
|
||||||
|
(ansi rb)
|
||||||
|
($env.LAST_EXIT_CODE)
|
||||||
|
] | str join)
|
||||||
|
} else { "" }
|
||||||
|
|
||||||
|
([$last_exit_code, (char space), $time_segment] | str join)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use nushell functions to define your right and left prompt
|
||||||
|
$env.PROMPT_COMMAND = {|| create_left_prompt }
|
||||||
|
# FIXME: This default is not implemented in rust code as of 2023-09-08.
|
||||||
|
$env.PROMPT_COMMAND_RIGHT = {|| create_right_prompt }
|
||||||
|
|
||||||
|
# The prompt indicators are environmental variables that represent
|
||||||
|
# the state of the prompt
|
||||||
|
$env.PROMPT_INDICATOR = {|| "> " }
|
||||||
|
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
|
||||||
|
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
|
||||||
|
$env.PROMPT_MULTILINE_INDICATOR = {|| "::: " }
|
||||||
|
|
||||||
|
# If you want previously entered commands to have a different prompt from the usual one,
|
||||||
|
# you can uncomment one or more of the following lines.
|
||||||
|
# This can be useful if you have a 2-line prompt and it's taking up a lot of space
|
||||||
|
# because every command entered takes up 2 lines instead of 1. You can then uncomment
|
||||||
|
# the line below so that previously entered commands show with a single `🚀`.
|
||||||
|
# $env.TRANSIENT_PROMPT_COMMAND = {|| "🚀 " }
|
||||||
|
# $env.TRANSIENT_PROMPT_INDICATOR = {|| "" }
|
||||||
|
# $env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT = {|| "" }
|
||||||
|
# $env.TRANSIENT_PROMPT_INDICATOR_VI_NORMAL = {|| "" }
|
||||||
|
# $env.TRANSIENT_PROMPT_MULTILINE_INDICATOR = {|| "" }
|
||||||
|
# $env.TRANSIENT_PROMPT_COMMAND_RIGHT = {|| "" }
|
||||||
|
|
||||||
|
# Specifies how environment variables are:
|
||||||
|
# - converted from a string to a value on Nushell startup (from_string)
|
||||||
|
# - converted from a value back to a string when running external commands (to_string)
|
||||||
|
# Note: The conversions happen *after* config.nu is loaded
|
||||||
|
$env.ENV_CONVERSIONS = {
|
||||||
|
"PATH": {
|
||||||
|
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
|
||||||
|
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
|
||||||
|
}
|
||||||
|
"Path": {
|
||||||
|
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
|
||||||
|
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directories to search for scripts when calling source or use
|
||||||
|
# The default for this is $nu.default-config-dir/scripts
|
||||||
|
$env.NU_LIB_DIRS = [
|
||||||
|
($nu.default-config-dir | path join 'scripts') # add <nushell-config-dir>/scripts
|
||||||
|
($nu.data-dir | path join 'completions') # default home for nushell completions
|
||||||
|
]
|
||||||
|
|
||||||
|
# Directories to search for plugin binaries when calling register
|
||||||
|
# The default for this is $nu.default-config-dir/plugins
|
||||||
|
$env.NU_PLUGIN_DIRS = [
|
||||||
|
($nu.default-config-dir | path join 'plugins') # add <nushell-config-dir>/plugins
|
||||||
|
]
|
||||||
|
|
||||||
|
# To add entries to PATH (on Windows you might use Path), you can use the following pattern:
|
||||||
|
# $env.PATH = ($env.PATH | split row (char esep) | prepend '/some/path')
|
||||||
|
# An alternate way to add entries to $env.PATH is to use the custom command `path add`
|
||||||
|
# which is built into the nushell stdlib:
|
||||||
|
use std "path add"
|
||||||
|
path add "/opt/homebrew/bin"
|
||||||
|
path add "/opt/homebrew/sbin"
|
||||||
|
path add ($env.HOME | path join ".turso")
|
||||||
|
path add ($env.HOME | path join ".local/share/mise/shims")
|
||||||
|
path add "/Users/omerxx/.local/bin"
|
||||||
|
|
||||||
|
|
||||||
|
# To load from a custom file you can use:
|
||||||
|
# source ($nu.default-config-dir | path join 'custom.nu')
|
||||||
|
|
||||||
|
mkdir ~/.cache/starship
|
||||||
|
starship init nu | save -f ~/.cache/starship/init.nu
|
||||||
|
zoxide init nushell | save -f ~/.zoxide.nu
|
||||||
|
mkdir ~/.cache/mise
|
||||||
|
^mise activate nu | save -f ~/.cache/mise/init.nu
|
||||||
|
|
||||||
|
$env.STARSHIP_CONFIG = "/Users/omerxx/.config/starship/starship.toml"
|
||||||
|
$env.CARAPACE_BRIDGES = 'zsh,fish,bash,inshellisense' # optional
|
||||||
|
mkdir ~/.cache/carapace
|
||||||
|
carapace _carapace nushell | save --force ~/.cache/carapace/init.nu
|
||||||
|
|
||||||
|
$env.EDITOR = "nvim"
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
],
|
],
|
||||||
"runtime.unicodeName": true,
|
"runtime.unicodeName": true,
|
||||||
"diagnostics.disable": [
|
"diagnostics.disable": [
|
||||||
"undefined-doc-name"
|
"undefined-doc-name",
|
||||||
|
"assign-type-mismatch"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- Tell Neovim to treat .ltx as regular tex
|
||||||
|
vim.filetype.add({
|
||||||
|
extension = {
|
||||||
|
ltx = "tex",
|
||||||
|
tex = "tex",
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
-- after/ftplugin/html.lua (or just :source % once)
|
||||||
|
local port = 8085
|
||||||
|
local root = vim.loop.cwd()
|
||||||
|
local job_id = nil
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("Serve", function()
|
||||||
|
-- kill previous python instance
|
||||||
|
if job_id then
|
||||||
|
vim.fn.jobstop(job_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- one-liner HTTP server + websocket broadcast
|
||||||
|
local script = ([[
|
||||||
|
import http.server, os, subprocess, threading, time, mimetypes, json
|
||||||
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
|
import websockets, asyncio, pathlib
|
||||||
|
|
||||||
|
ROOT = %q
|
||||||
|
PORT = %d
|
||||||
|
clients = set()
|
||||||
|
|
||||||
|
class Handler(SimpleHTTPRequestHandler):
|
||||||
|
def log_message(self, fmt, *args): pass # shut up
|
||||||
|
def do_GET(self):
|
||||||
|
if self.path == '/':
|
||||||
|
self.path = '/index.html'
|
||||||
|
# inject tiny reload script into every HTML
|
||||||
|
if self.path.endswith('.html'):
|
||||||
|
ctype = 'text/html; charset=utf-8'
|
||||||
|
with open(os.path.join(ROOT, self.path.lstrip('/')), 'rb') as f:
|
||||||
|
content = f.read().decode()
|
||||||
|
inject = '<script>let ws=new WebSocket("ws://localhost:%d/ws");ws.onmessage=()=>location.reload()</script>'
|
||||||
|
content = content.replace('</body>', inject .. '</body>')
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', ctype)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(content.encode())
|
||||||
|
else:
|
||||||
|
super().do_GET()
|
||||||
|
|
||||||
|
async def ws_handler(websocket):
|
||||||
|
clients.add(websocket)
|
||||||
|
try:
|
||||||
|
await websocket.wait_closed()
|
||||||
|
finally:
|
||||||
|
clients.remove(websocket)
|
||||||
|
|
||||||
|
async def websocket_main():
|
||||||
|
await websockets.serve(ws_handler, "localhost", %d, path="/ws")
|
||||||
|
|
||||||
|
def broadcast_reload():
|
||||||
|
for c in clients:
|
||||||
|
asyncio.run(c.send("reload"))
|
||||||
|
clients.clear()
|
||||||
|
|
||||||
|
def reload_loop():
|
||||||
|
while True:
|
||||||
|
time.sleep(0.05)
|
||||||
|
try:
|
||||||
|
subprocess.check_call(["inotifywait", "-e", "close_write", "-t", "0", ROOT],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
broadcast_reload()
|
||||||
|
except: pass # inotify not installed → noop (manual F5 still works)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
threading.Thread(target=reload_loop, daemon=True).start()
|
||||||
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||||
|
threading.Thread(target=lambda: asyncio.run(websocket_main()), daemon=True).start()
|
||||||
|
HTTPServer(("", PORT), Handler).serve_forever()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
]]):format(root, port, port, port)
|
||||||
|
|
||||||
|
job_id = vim.fn.jobstart({ "python", "-c", script }, { stdout_buffered = true })
|
||||||
|
vim.fn.jobstart({ "sleep", "0.2" }, {
|
||||||
|
on_exit = function()
|
||||||
|
vim.ui.open(("http://localhost:%d"):format(port))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.notify("Serving → http://localhost:" .. port, vim.log.levels.INFO)
|
||||||
|
end, {})
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("ServeStop", function()
|
||||||
|
if job_id then
|
||||||
|
vim.fn.jobstop(job_id)
|
||||||
|
job_id = nil
|
||||||
|
end
|
||||||
|
end, {})
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
-- Improved Neovim ftplugin for mail with robust abook integration
|
||||||
|
-- Debugging enabled: check :messages for logs
|
||||||
|
|
||||||
|
local function log(msg)
|
||||||
|
-- print("[abook] " .. msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
log("ftplugin/mail.lua loaded for filetype: " .. vim.bo.filetype)
|
||||||
|
|
||||||
|
-- Omnifunc completion for email addresses
|
||||||
|
function _G.mail_complete(findstart, base)
|
||||||
|
if findstart == 1 then
|
||||||
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
local col = vim.fn.col(".")
|
||||||
|
local line_to_cursor = line:sub(1, col - 1)
|
||||||
|
|
||||||
|
-- Detect if we are on a header line that takes email addresses
|
||||||
|
if not line_to_cursor:match("^%s*[Tt][Oo]:") and
|
||||||
|
not line_to_cursor:match("^%s*[Cc][Cc]:") and
|
||||||
|
not line_to_cursor:match("^%s*[Bb][Cc][Cc]:") and
|
||||||
|
not line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") and
|
||||||
|
not line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:") then
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find start of current address (after comma or space)
|
||||||
|
local start = line_to_cursor:reverse():find("[%s,]")
|
||||||
|
if start then
|
||||||
|
return col - start
|
||||||
|
else
|
||||||
|
return line_to_cursor:find(":") or 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Query abook
|
||||||
|
log("Querying abook for: " .. base)
|
||||||
|
local cmd = string.format('abook --mutt-query "%s" 2>/dev/null', base)
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
if not handle then return {} end
|
||||||
|
|
||||||
|
local matches = {}
|
||||||
|
for line in handle:lines() do
|
||||||
|
if line ~= "" and not line:match("^%s*$") then
|
||||||
|
local email, name = line:match("^([^\t]+)\t([^\t]+)")
|
||||||
|
if email then
|
||||||
|
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
|
||||||
|
table.insert(matches, { word = formatted, abbr = line:gsub("\t", " | "):gsub("%s+$", "") })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
handle:close()
|
||||||
|
log("Found " .. #matches .. " matches")
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set omnifunc locally
|
||||||
|
vim.opt_local.omnifunc = "v:lua.mail_complete"
|
||||||
|
|
||||||
|
-- Trigger completion on Tab in header lines
|
||||||
|
vim.keymap.set("i", "<Tab>", function()
|
||||||
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
local col = vim.fn.col(".")
|
||||||
|
local line_to_cursor = line:sub(1, col - 1)
|
||||||
|
|
||||||
|
local is_header = line_to_cursor:match("^%s*[Tt][Oo]:") or
|
||||||
|
line_to_cursor:match("^%s*[Cc][Cc]:") or
|
||||||
|
line_to_cursor:match("^%s*[Bb][Cc][Cc]:") or
|
||||||
|
line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") or
|
||||||
|
line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:")
|
||||||
|
|
||||||
|
if is_header then
|
||||||
|
return "<C-x><C-o>"
|
||||||
|
end
|
||||||
|
|
||||||
|
return "<Tab>"
|
||||||
|
end, { expr = true, buffer = true })
|
||||||
|
|
||||||
|
-- Interactive picker
|
||||||
|
local function pick_email()
|
||||||
|
log("Manual picker triggered")
|
||||||
|
local handle = io.popen('abook --mutt-query "" 2>/dev/null')
|
||||||
|
if not handle then
|
||||||
|
vim.notify("Could not query abook", vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
for line in handle:lines() do
|
||||||
|
if line ~= "" and not line:match("^%s*$") then
|
||||||
|
local email, name = line:match("^([^\t]+)\t([^\t]+)")
|
||||||
|
if email then
|
||||||
|
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
|
||||||
|
table.insert(items, { display = line:gsub("\t", " | "):gsub("%s+$", ""), value = formatted })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
handle:close()
|
||||||
|
|
||||||
|
if #items == 0 then
|
||||||
|
vim.notify("No addresses in abook", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(items, function(a, b) return a.display < b.display end)
|
||||||
|
|
||||||
|
-- Use Snacks.picker if available, otherwise fallback to vim.ui.select
|
||||||
|
if _G.Snacks and Snacks.picker then
|
||||||
|
Snacks.picker.select(items, {
|
||||||
|
prompt = "Select Recipient",
|
||||||
|
format_item = function(item) return item.display end,
|
||||||
|
}, function(choice)
|
||||||
|
if choice then
|
||||||
|
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||||
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
local before = line:sub(1, col)
|
||||||
|
local after = line:sub(col + 1)
|
||||||
|
if before:match("[:%,]$") then choice.value = " " .. choice.value end
|
||||||
|
vim.api.nvim_set_current_line(before .. choice.value .. after)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
vim.ui.select(items, {
|
||||||
|
prompt = "Select Recipient:",
|
||||||
|
format_item = function(item) return item.display end,
|
||||||
|
}, function(choice)
|
||||||
|
if choice then
|
||||||
|
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||||
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
local before = line:sub(1, col)
|
||||||
|
local after = line:sub(col + 1)
|
||||||
|
if before:match("[:%,]$") then choice.value = " " .. choice.value end
|
||||||
|
vim.api.nvim_set_current_line(before .. choice.value .. after)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Keybinds
|
||||||
|
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
|
||||||
|
vim.keymap.set("n", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
-- In your init.lua or after lazy setup
|
||||||
|
vim.opt.termguicolors = true
|
||||||
|
|
||||||
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
||||||
if not vim.loop.fs_stat(lazypath) then
|
if not vim.loop.fs_stat(lazypath) then
|
||||||
vim.fn.system({
|
vim.fn.system({
|
||||||
@@ -11,17 +14,37 @@ if not vim.loop.fs_stat(lazypath) then
|
|||||||
end
|
end
|
||||||
vim.opt.rtp:prepend(lazypath)
|
vim.opt.rtp:prepend(lazypath)
|
||||||
|
|
||||||
|
-- Enable spell check for mail files
|
||||||
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
|
pattern = "mail",
|
||||||
|
callback = function()
|
||||||
|
vim.opt_local.spell = true
|
||||||
|
vim.opt_local.spelllang = "en_us,de" -- Add languages you need
|
||||||
|
end,
|
||||||
|
})
|
||||||
-- Only enable spell checking for specific filetypes
|
-- Only enable spell checking for specific filetypes
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
pattern = { "markdown", "text", "gitcommit", "tex" },
|
pattern = { "mail", "markdown", "text", "gitcommit", "tex", "plaintext" },
|
||||||
callback = function()
|
callback = function()
|
||||||
vim.opt_local.spell = true
|
vim.opt_local.spell = true
|
||||||
vim.opt_local.spelllang = "en_us,de"
|
vim.opt_local.spelllang = "en_us,de"
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vim.filetype.add({
|
||||||
|
extension = {
|
||||||
|
tex = "tex",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
require("vim-options")
|
require("vim-options")
|
||||||
require("lazy").setup("plugins")
|
|
||||||
|
-- SINGLE lazy.setup call - combine everything here
|
||||||
|
require("lazy").setup({
|
||||||
|
-- Load plugins from the plugins directory
|
||||||
|
{ import = "plugins" },
|
||||||
|
})
|
||||||
|
|
||||||
require("lualine").setup({
|
require("lualine").setup({
|
||||||
sections = {
|
sections = {
|
||||||
lualine_x = {
|
lualine_x = {
|
||||||
@@ -33,10 +56,18 @@ require("lualine").setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require("lazy").setup({
|
|
||||||
{
|
-- In your init.lua or after lazy setup
|
||||||
"tzachar/cmp-tabnine",
|
local theme_file = vim.fn.stdpath("config") .. "/lua/current_theme.lua"
|
||||||
build = "./install.sh",
|
if vim.fn.filereadable(theme_file) == 1 then
|
||||||
dependencies = "hrsh7th/nvim-cmp",
|
dofile(theme_file)
|
||||||
},
|
else
|
||||||
})
|
vim.cmd.colorscheme("catppuccin") -- fallback
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
|
||||||
|
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
|
||||||
|
vim.api.nvim_set_hl(0, "NormalNC", { bg = "none" })
|
||||||
|
vim.api.nvim_set_hl(0, "SignColumn", { bg = "none" })
|
||||||
|
vim.api.nvim_set_hl(0, "FloatBorder", { bg = "none" })
|
||||||
|
vim.api.nvim_set_hl(0, "LineNr", { bg = "none" })
|
||||||
|
|||||||
@@ -1,60 +1,76 @@
|
|||||||
{
|
{
|
||||||
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
|
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
|
||||||
"LuaSnip": { "branch": "master", "commit": "fb525166ccc30296fb3457441eb979113de46b00" },
|
"LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
|
||||||
|
"R.nvim": { "branch": "main", "commit": "fc42c220764268cfa28a2a17a776bb5f4eb94f46" },
|
||||||
"barbecue": { "branch": "main", "commit": "cd7e7da622d68136e13721865b4d919efd6325ed" },
|
"barbecue": { "branch": "main", "commit": "cd7e7da622d68136e13721865b4d919efd6325ed" },
|
||||||
"catppuccin": { "branch": "main", "commit": "ce8d176faa4643e026e597ae3c31db59b63cef09" },
|
"catppuccin": { "branch": "main", "commit": "426dbebe06b5c69fd846ceb17b42e12f890aedf1" },
|
||||||
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
|
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
|
||||||
"cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
|
"cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
|
||||||
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
|
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
|
||||||
"cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" },
|
"cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" },
|
||||||
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
|
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
|
||||||
"cmp-tabnine": { "branch": "main", "commit": "c0167cdc86c15e782c5461ee62aebee89231c2ed" },
|
"cmp-r": { "branch": "main", "commit": "70bfe8f4c062acc10266e24825439c009a0b1b89" },
|
||||||
|
"cmp-vimtex": { "branch": "master", "commit": "c09ca05bfa0641754516f13294d73d3799a02fc8" },
|
||||||
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
|
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
|
||||||
"conform.nvim": { "branch": "master", "commit": "5f5152fae39a57e3a04cbce09fb21e10c49c9d95" },
|
"conform.nvim": { "branch": "master", "commit": "dca1a190aa85f9065979ef35802fb77131911106" },
|
||||||
"dashboard-nvim": { "branch": "master", "commit": "0775e567b6c0be96d01a61795f7b64c1758262f6" },
|
"dashboard-nvim": { "branch": "master", "commit": "f787e3462c2ee2b6117b17c1aa4ddf66cb6f57fe" },
|
||||||
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
|
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
|
||||||
|
"dracula.nvim": { "branch": "main", "commit": "ae752c13e95fb7c5f58da4b5123cb804ea7568ee" },
|
||||||
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
|
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
|
||||||
|
"everforest": { "branch": "master", "commit": "aeef62ee97872d2557d25904d160ec93a4a355f8" },
|
||||||
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
|
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
|
||||||
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
|
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
|
||||||
"fzf-lua": { "branch": "main", "commit": "9d579feab4d3035627150e5e9b6e8fbf5e814ef6" },
|
"fzf-lua": { "branch": "main", "commit": "17e3507b788699776ba6d2f8dd101ec177f37a96" },
|
||||||
"gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" },
|
"gitsigns.nvim": { "branch": "main", "commit": "dd3f588bacbeb041be6facf1742e42097f62165d" },
|
||||||
"grapple.nvim": { "branch": "main", "commit": "b41ddfc1c39f87f3d1799b99c2f0f1daa524c5f7" },
|
"grapple.nvim": { "branch": "main", "commit": "b41ddfc1c39f87f3d1799b99c2f0f1daa524c5f7" },
|
||||||
"harpoon": { "branch": "harpoon2", "commit": "87b1a3506211538f460786c23f98ec63ad9af4e5" },
|
"gruvbox-mat": { "branch": "master", "commit": "11d779b26a9ab2b3db8c22c6ac9fb6e8ed4fea79" },
|
||||||
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
|
"indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
|
||||||
|
"kanagawa.nvim": { "branch": "master", "commit": "8ad3b4cdcc804b332c32db8f9743667e1bb82b99" },
|
||||||
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||||
"lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" },
|
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
|
||||||
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
|
"live-preview.nvim": { "branch": "main", "commit": "c1fcf75c5f9c9c01dd392852de44204b60f1b5b1" },
|
||||||
"luvit-meta": { "branch": "main", "commit": "0ea4ff636c5bb559ffa78108561d0976f4de9682" },
|
"lualine.nvim": { "branch": "master", "commit": "131a558e13f9f28b15cd235557150ccb23f89286" },
|
||||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "8f1a3e6eecb638817e8999aaa16ada27cd54d867" },
|
"luvit-meta": { "branch": "main", "commit": "cc9b2d412d2fbd30b94a70cfc214c2a3be27a0a2" },
|
||||||
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
|
"mason-lspconfig.nvim": { "branch": "main", "commit": "0c2823e0418f3d9230ff8b201c976e84de1cb401" },
|
||||||
"mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" },
|
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
|
||||||
"neo-tree.nvim": { "branch": "v3.x", "commit": "f3df514fff2bdd4318127c40470984137f87b62e" },
|
"mason.nvim": { "branch": "main", "commit": "cb8445f8ce85d957416c106b780efd51c6298f89" },
|
||||||
"neogit": { "branch": "master", "commit": "d8bf9102692250193b855acd9025a826f1af2729" },
|
"neo-tree.nvim": { "branch": "v3.x", "commit": "84c75e7a7e443586f60508d12fc50f90d9aee14e" },
|
||||||
|
"neogit": { "branch": "master", "commit": "e74dfb42c04b493031f323aec8fa5f28b0427b9e" },
|
||||||
|
"nightfox.nvim": { "branch": "main", "commit": "26b61b1f856ec37cae3cb64f5690adb955f246a1" },
|
||||||
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
|
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
|
||||||
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
|
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
|
||||||
"nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" },
|
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
|
||||||
"nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" },
|
"nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
|
||||||
"nvim-lint": { "branch": "master", "commit": "7a64f4067065c16a355d40d0d599b8ca6b25de6d" },
|
"nvim-colorizer.lua": { "branch": "master", "commit": "a065833f35a3a7cc3ef137ac88b5381da2ba302e" },
|
||||||
"nvim-lspconfig": { "branch": "master", "commit": "d20d83b3f24f5884da73a9fc92fdc47e778b8d0d" },
|
"nvim-lint": { "branch": "master", "commit": "eab58b48eb11d7745c11c505e0f3057165902461" },
|
||||||
"nvim-navic": { "branch": "master", "commit": "7d914a39a1ef8f4e22c2c4381abeef7c556f5a13" },
|
"nvim-lspconfig": { "branch": "master", "commit": "31026a13eefb20681124706a79fc1df6bf11ab27" },
|
||||||
|
"nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" },
|
||||||
"nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" },
|
"nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" },
|
||||||
"nvim-spectre": { "branch": "master", "commit": "72f56f7585903cd7bf92c665351aa585e150af0f" },
|
"nvim-spectre": { "branch": "master", "commit": "72f56f7585903cd7bf92c665351aa585e150af0f" },
|
||||||
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
|
"nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
|
||||||
"nvim-tmux-navigation": { "branch": "main", "commit": "4898c98702954439233fdaf764c39636681e2861" },
|
"nvim-tmux-navigation": { "branch": "main", "commit": "4898c98702954439233fdaf764c39636681e2861" },
|
||||||
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
|
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
|
||||||
"nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
|
"nvim-treesitter-textobjects": { "branch": "main", "commit": "851e865342e5a4cb1ae23d31caf6e991e1c99f1e" },
|
||||||
"nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" },
|
"nvim-ts-context-commentstring": { "branch": "main", "commit": "6141a40173c6efa98242dc951ed4b6f892c97027" },
|
||||||
"nvim-web-devicons": { "branch": "master", "commit": "6788013bb9cb784e606ada44206b0e755e4323d7" },
|
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
|
||||||
|
"nvim-web-devicons": { "branch": "master", "commit": "4fc505ac7bd7692824a142e96e5f529c133862f8" },
|
||||||
"obsidian.nvim": { "branch": "main", "commit": "ae1f76a75c7ce36866e1d9342a8f6f5b9c2caf9b" },
|
"obsidian.nvim": { "branch": "main", "commit": "ae1f76a75c7ce36866e1d9342a8f6f5b9c2caf9b" },
|
||||||
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
|
"onedark-warm": { "branch": "master", "commit": "df4792accde9db0043121f32628bcf8e645d9aea" },
|
||||||
|
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
|
||||||
"portal.nvim": { "branch": "main", "commit": "77d9d53fec945bfa407d5fd7120f1b4f117450ed" },
|
"portal.nvim": { "branch": "main", "commit": "77d9d53fec945bfa407d5fd7120f1b4f117450ed" },
|
||||||
"rainbow-delimiters.nvim": { "branch": "master", "commit": "8aafe2cbd89cd4090f573a98cab6b20366576fde" },
|
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
|
||||||
"render-markdown.nvim": { "branch": "main", "commit": "07d088bf8bdadd159eb807b90eaee86a4778383f" },
|
"rainbow-delimiters.nvim": { "branch": "master", "commit": "08783ec022e7ddefe0f12a16f1ac4968f55478b0" },
|
||||||
|
"render-markdown.nvim": { "branch": "main", "commit": "3f3eea97b80839f629c951ca660ffd125bfa5b34" },
|
||||||
|
"rose-pine": { "branch": "main", "commit": "6a961effd67f6130d36df6d1c05c48c739796dd2" },
|
||||||
|
"snacks.nvim": { "branch": "main", "commit": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e" },
|
||||||
|
"sonokai": { "branch": "master", "commit": "b023c5280b16fe2366f5e779d8d2756b3e5ee9c3" },
|
||||||
"telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
|
"telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
|
||||||
"telescope.nvim": { "branch": "master", "commit": "e709d31454ee6e6157f0537f861f797bd44c0bad" },
|
"telescope.nvim": { "branch": "master", "commit": "f04ab730b8f9c6bf3f54a206d0dcddfd70c52d59" },
|
||||||
|
"todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
|
||||||
|
"toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" },
|
||||||
|
"tokyonight": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
|
||||||
"trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" },
|
"trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" },
|
||||||
"vim-test": { "branch": "master", "commit": "aa619692ff48a3cf3e6bdb893765039488d4e5f3" },
|
"vimtex": { "branch": "master", "commit": "97e11bd4f56d46a87f8593d6ccb27820e19c4ab0" },
|
||||||
"vimux": { "branch": "master", "commit": "614f0bb1fb598f97accdcea71d5f7b18d7d62436" },
|
|
||||||
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
|
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
|
||||||
"yazi.nvim": { "branch": "main", "commit": "d87cd885e06d50e1bb5607e48d96c1094d3b10e3" }
|
"yazi.nvim": { "branch": "main", "commit": "aada50881aca7d13422e02a6681b8f0a0ec9a75c" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
-- vim.cmd.colorscheme("rose-pine")
|
||||||
|
-- vim.cmd.colorscheme("sonokai")
|
||||||
|
-- vim.cmd.colorscheme("one-dark")
|
||||||
|
-- vim.cmd.colorscheme("nightfox")
|
||||||
|
-- vim.cmd.colorscheme("kanagawa")
|
||||||
|
vim.cmd.colorscheme("gruvbox-material")
|
||||||
|
-- vim.cmd.colorscheme("everforest")
|
||||||
|
-- vim.cmd.colorscheme("dracula")
|
||||||
|
-- vim.cmd.colorscheme("carbonfox")
|
||||||
|
-- vim.cmd.colorscheme("tokyonight")
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
-- ~/.config/nvim/lua/plugins/blink.lua
|
||||||
|
return {
|
||||||
|
"saghen/blink.cmp",
|
||||||
|
version = "1.*",
|
||||||
|
build = "cargo build --release",
|
||||||
|
lazy = false,
|
||||||
|
|
||||||
|
dependencies = {
|
||||||
|
"L3MON4D3/LuaSnip",
|
||||||
|
"rafamadriz/friendly-snippets",
|
||||||
|
},
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
fuzzy = { implementation = "prefer_rust" },
|
||||||
|
|
||||||
|
sources = {
|
||||||
|
min_keyword_length = 2,
|
||||||
|
default = { "lsp", "snippets", "buffer", "path", "lua" },
|
||||||
|
per_filetype = {
|
||||||
|
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
completion = {
|
||||||
|
accept = { auto_brackets = { enabled = true } },
|
||||||
|
trigger = { show_on_trigger_character = true },
|
||||||
|
list = {
|
||||||
|
selection = {
|
||||||
|
preselect = function(ctx) return ctx.mode ~= "cmdline" end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
documentation = {
|
||||||
|
auto_show = true,
|
||||||
|
auto_show_delay_ms = 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
signature = { enabled = true, window = { border = "rounded" } },
|
||||||
|
cmdline = { enabled = false },
|
||||||
|
},
|
||||||
|
|
||||||
|
config = function(_, opts)
|
||||||
|
require("blink.cmp").setup(opts)
|
||||||
|
end,
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
-- ~/.config/nvim/lua/plugins/blink.lua
|
||||||
|
return {
|
||||||
|
"saghen/blink.cmp",
|
||||||
|
version = "1.*",
|
||||||
|
build = "cargo build --release",
|
||||||
|
lazy = false,
|
||||||
|
|
||||||
|
dependencies = {
|
||||||
|
"L3MON4D3/LuaSnip",
|
||||||
|
"rafamadriz/friendly-snippets",
|
||||||
|
},
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
fuzzy = { implementation = "prefer_rust" },
|
||||||
|
|
||||||
|
sources = {
|
||||||
|
min_keyword_length = 1,
|
||||||
|
default = { "lsp", "snippets", "buffer", "path" },
|
||||||
|
providers = {
|
||||||
|
lsp = { name = "LSP", opts = { max_items = 200, score_offset = 0 } },
|
||||||
|
buffer = { name = "Buffer", opts = { max_items = 100 } },
|
||||||
|
},
|
||||||
|
per_filetype = {
|
||||||
|
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
completion = {
|
||||||
|
accept = { auto_brackets = { enabled = true } },
|
||||||
|
trigger = { show_on_trigger_character = true },
|
||||||
|
list = { selection = "auto_insert" },
|
||||||
|
documentation = { auto_show = true, auto_show_delay_ms = 200 },
|
||||||
|
ghost_text = { enabled = true },
|
||||||
|
},
|
||||||
|
|
||||||
|
signature = { enabled = true, window = { border = "rounded" } },
|
||||||
|
cmdline = { enabled = false },
|
||||||
|
|
||||||
|
-- Tab cycles through items and accepts
|
||||||
|
keymap = {
|
||||||
|
preset = "enter",
|
||||||
|
["<Tab>"] = {
|
||||||
|
"select_next",
|
||||||
|
"accept",
|
||||||
|
"snippet_forward",
|
||||||
|
"fallback",
|
||||||
|
},
|
||||||
|
["<S-Tab>"] = {
|
||||||
|
"select_prev",
|
||||||
|
"snippet_backward",
|
||||||
|
"fallback",
|
||||||
|
},
|
||||||
|
["<CR>"] = { "accept", "fallback" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
config = function(_, opts)
|
||||||
|
require("blink.cmp").setup(opts)
|
||||||
|
end,
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
return {
|
|
||||||
{
|
|
||||||
"hrsh7th/cmp-path", -- file and folders
|
|
||||||
"hrsh7th/cmp-buffer", -- words from the current buffer
|
|
||||||
"hrsh7th/cmp-nvim-lsp", -- LSP-based autocompletions
|
|
||||||
"hrsh7th/cmp-cmdline", -- Command-line autocompletions
|
|
||||||
"tzachar/cmp-tabnine"--, build = "./install.sh",
|
|
||||||
},
|
|
||||||
}
|
|
||||||