Compare commits

..
Author SHA1 Message Date
Karmaa 10bc491a9f Dev 2.0 (#23)
* Added user system with database features. This is fairly experimental and does not include dockerfile to automatically generate a mongodb. This should be in future commits along with ability to save hosts branching off this database feature.

* Updated README, fixed a few bugs with user creation, and added docker support to run MongoDB (needs testing)

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in installing MongoDB

* Changes to Dockerfile to fix error in connecting to sockets

* Update README.md

* Changes to connection system to support docker

* Changes to connection system to support docker

* Changes to connection system to support docker

* Changes to connection system to support docker

* Save hosts to tabs (very early version, not that many issues not just not very feature rich and has a poor UI that will be improved with more features)

* Updated launchpad UI to be expandable in the future. Updated UI for the hosts to be able to easily configure them. They stil need organizational system (folders, etc.)

* Better encryption for everything, new session login, rewrote a lot of database code changing its storage methods. Prepared for release of 2.0.

* Updated database connection method.

* Updated Profile modal to show username text more clearly

* Updated Profile modal to show username text more clearly

* Fixed control v pasting formating. Reorganized location of scripts. Visbile password and confirm password. Guest login. OpenSSH key authentication. Optional to remember password. Serach for host viewer.

* Waits for user to be able to log in. Improved UI for profile, edit and add host, and added organizational features to the host app (Folders, search, etc.)

* Updated various names for rsa keys to public keys, fixes ssh not connecting, better timing for editing host.

* Added ability to share hosts. Fixed up overall UI errors in console and cleaned up code for release.

* Fix GitHub build errors

* Attempt #1 to auto compile MongoDB into the build to exclude it from the compose.

* Attempt #2 to auto compile MongoDB into the build to exclude it from the compose.

* Attempt #3 to auto compile MongoDB into the build to exclude it from the compose.

* Attempt #3 to auto compile MongoDB into the build to exclude it from the compose.
2025-03-16 14:17:55 -05:00
Karmaa 9aa83c24ed Update README.md 2025-03-12 15:54:19 -05:00
KarmaaandDale Driver 54f03d73ce Dev 1.1 (#19)
* Update server.cjs (#17)

Redact only sensitive info for logging

* Changes to README.md for clear support paths.

* Switched to UTF-8 fixing non-english character bugs, revamped console logging system, fixed misc console errors, and fixed copy/paste. Warning: the terminal sizing in this version is very buggy so be warned.

* Fixed multi-line command issues with switching between split and not split.

---------

Co-authored-by: Dale Driver <firestrife23@users.noreply.github.com>
2025-03-09 18:51:05 -05:00
KarmaaandDale Driver b0062343c7 Dev 1.1 (#18)
* Update server.cjs (#17)

Redact only sensitive info for logging

* Changes to README.md for clear support paths.

* Switched to UTF-8 fixing non-english character bugs, revamped console logging system, fixed misc console errors, and fixed copy/paste. Warning: the terminal sizing in this version is very buggy so be warned.

* Fixed multi-line command issues with switching between split and not split.

* Shifted terminal down and to the right so its not squished up in the top left corner. Fix scroll wheel sizing and looks. Prepared for this bug update release.

---------

Co-authored-by: Dale Driver <firestrife23@users.noreply.github.com>
2025-03-09 18:46:09 -05:00
Karmaa 09677caa26 Update README.md 2025-03-06 20:19:08 -06:00
Karmaa 7dc1bce272 Merge branch 'main' into dev-1.1
Merge dev-1.1 into main branch.
2025-03-06 20:00:04 -06:00
Karmaa 58ba96654a Fixed discord buton in README.md 2025-03-06 19:48:37 -06:00
Karmaa 0b00c26f8a Fixed small bug in terminal resizing. Updated readme with discord. 2025-03-06 19:45:17 -06:00
Karmaa 9ff199ea96 Final preparations for release. Fixed auto resizing bug when smaller terminal size. 2025-03-06 18:43:27 -06:00
Karmaa 0d464cdf56 Added RSA support for authentication. Furthered preparations for release. 2025-03-06 17:52:27 -06:00
Karmaa 963e54bf15 Fixed up some UI issues and auto-sizing for the terminal. Finished up preparations for full release. 2025-03-05 22:49:34 -06:00
Karmaa 62262e5bf3 Fixed entrypoint to start server automatically and made some scroll wheel changes. Getting very close to release point. 2025-03-05 16:29:57 -06:00
Karmaa e800064f05 Updated conection process to work hopfully with docker. 2025-03-05 16:15:51 -06:00
Karmaa 713d43fc52 Made some UI changes and code cleanup. Split screen working great, readying for release for user testing (without persistence or launchpad for now). 2025-03-05 14:46:46 -06:00
Karmaa a6295e5e7e Fixed up the split screen a lot. 3+ screens at once causes inability to unsplit. Still a WIP. 2025-03-05 00:43:57 -06:00
Karmaa 2038a84c15 Fixed up split screen a little. Still buggy. Made some UI changes too. 2025-03-02 00:04:21 -06:00
Karmaa cf8fff572a Added initial split screen mode and overall better UI sizing for terminals. (very buggy) 2025-02-26 00:58:22 -06:00
Karmaa cf5f0c41eb Another UI overall. Shift to launchpad, removed sidebar, turned everything into components. 2025-02-25 00:08:38 -06:00
Karmaa b43ca54fa0 UI Overhaul (Switch to tailwindcss and MUI) 2025-02-23 22:46:30 -06:00
Karmaa 08dbfdaf8f Attempt #7 to fix CORS issue and docker build. 2025-02-22 01:40:45 -06:00
Karmaa ba502de450 Attempt #6 to fix CORS issue and docker build. 2025-02-22 01:25:31 -06:00
Karmaa 226cdcb7a0 Attempt #5 to fix CORS issue and docker build. 2025-02-22 01:12:45 -06:00
Karmaa a9d510aba4 Attempt #4 to fix CORS issue and docker build. 2025-02-22 00:59:38 -06:00
Karmaa 5c5fe57dad Attempt #3 to fix CORS issue and docker build. 2025-02-22 00:39:34 -06:00
Karmaa 1addd43039 Attempt #2 to fix CORS issue and docker build. 2025-02-22 00:24:47 -06:00
Karmaa 946f5daaa9 Attempt #1 to fix CORS issue and docker build. 2025-02-22 00:18:28 -06:00
Karmaa e42c8c9788 Added ARM support 2025-02-21 23:59:40 -06:00
Karmaa a6b03aa792 Updated socket connection code to support docker. 2025-02-09 21:35:45 -06:00
Karmaa 175225dcd7 Updated socket connection code to support docker. Also updated dockerfile and entrypoint to support new file structure. 2025-02-09 21:16:31 -06:00
Karmaa f2918e02f6 Updated socket connection code to support docker 2025-02-09 21:07:09 -06:00
Karmaa cb18a0cd87 Updated nginx and connection to support local and docker runtime 2025-02-09 20:29:39 -06:00
Karmaa 98a45e9161 Updated dockerfile for new file structure 2025-02-09 02:23:31 -06:00
Karmaa 95c2994b3c Updated dockerfile for new file structure 2025-02-09 02:15:19 -06:00
Karmaa 0b302fc860 Updated dockerfile for new file structure 2025-02-09 02:13:44 -06:00
Karmaa c5429ff2e8 Updated dockerfile for new file structure 2025-02-09 02:05:20 -06:00
Karmaa 9ab46cddd4 Updated dockerfile for new file structure 2025-02-09 02:03:39 -06:00
Karmaa 128ecbea04 Updated dockerfile for new file structure 2025-02-09 02:02:35 -06:00
Karmaa 9f7331d72a Updated dockerfile for new file structure 2025-02-09 02:00:30 -06:00
Karmaa 968e0cfeea Updated dockerfile for new file structure 2025-02-09 01:59:24 -06:00
Karmaa c7e06f422d Updated dockerfile for new file structure 2025-02-09 01:57:49 -06:00
Karmaa d799b8fe42 Updated dockerfile for new file structure 2025-02-09 01:55:36 -06:00
Karmaa e058892118 Updated docker compose for new file structure 2025-02-09 01:50:59 -06:00
Karmaa 33060eb1e4 Fixed tab spacing, nano reset, and coloring. 2025-02-09 01:48:59 -06:00
Karmaa 3b0eddca27 Add LICENSE 2025-02-06 23:48:16 -06:00
Karmaa 8afadf6176 Initial version 1.1 2025-02-06 23:47:21 -06:00
Karmaa 002683a2f0 Merge pull request #10 from LukeGus/release-1.0
Merge pull request #8 from LukeGus/release-1.0 (#9)
2024-12-12 21:20:18 -06:00
Karmaa 24dcdc6751 Merge pull request #8 from LukeGus/release-1.0 (#9) 2024-12-12 21:19:47 -06:00
Karmaa bbaa9bf226 Merge pull request #8 from LukeGus/release-1.0
Release 1.0
2024-12-12 19:51:16 -06:00
Karmaa 37e314edff Update App.jsx 2024-12-12 19:50:59 -06:00
Karmaa b2f5459817 Merge pull request #7 from LukeGus/main
Release 1.0
2024-12-12 19:48:05 -06:00
Karmaa cd53d15a04 Merge branch 'main' of https://github.com/LukeGus/Termix 2024-12-12 19:45:46 -06:00
Karmaa a17e72eec8 Update workspace.xml 2024-12-12 19:44:33 -06:00
Karmaa 82b57a6af5 Merge branch 'release-1.0' 2024-12-12 19:44:27 -06:00
LukeGus 3c474d3b52 Fix stupid error (forgot to change websocket to wsUrl) 2024-12-12 19:22:39 -06:00
Karmaa 15e1fd215e Release 1.0 (#3)
* Nano and timeout error fix #1

* Nano and timeout error fix #2

* Nano and timeout error fix #3

* Nano and timeout error fix #4

* Nano and timeout error fix #5

* Nano and timeout error fix #6

* Nano and timeout error fix #7 (plus css changes for hide buttons)

* Nano and timeout error fix #8 (zoom method)

* Nano zoom fix #9

* Nano zoom fix #10

* Nano zoom fix #11

* Nano zoom fix #12

* Nano zoom fix #13

* Nano zoom fix #14

* Nano zoom fix #15

* Nano zoom fix #16 (ssh-2-promise)

* Nano zoom fix #17 (ssh-2-promise port  fix)

* Full return back to old code with minor fixes

* Terminal size fix #18 (rows & cols fix)

* Test ntfy build notification system

* Silent resize cmds & Auto resize terminal #1

* Silent resize cmds & Auto resize terminal #2

* Docker build update

* Docker build update #2

* Docker build update #3

* Docker build update #4 (cache and cleanup)

* Docker build update #5 (cache and cleanup)

* Docker build update #6 (cache and cleanup)

* Docker build update #7 (final)

* Docker build update #8 (nevermind not finanl)

* Docker build update #9 (nevermind not finanl)

* Docker build update #10

* Docker build update #10 (I hope final)

* Docker build update #10 (actual final)

* Release 1.0!!!!

* Repo clean-up

* Remove files ignored by .gitignore

* Change project name, create logo, change README.md to be soon updated.

* Update README.md #1 (also added MIT License)

* Update README.md #2

* Update README.md #3

* Update README.md #4

* Update README.md #5

* Update README.md #6

* Update README.md #7

* Update README.md

* Add images

* Update image name

* Update README.md

* Final changes to release-1.0

* Update README.md

* Update name (need to fix logo)

* Final updates (I hope for real this time)

* Fix web-socket timeout.

* Fix timeout on close.
2024-12-12 18:46:43 -06:00
LukeGus 4d8263a788 Fix timeout on close. 2024-12-10 21:32:58 -06:00
LukeGus 2061ac5e3b Fix web-socket timeout. 2024-12-10 21:28:39 -06:00
LukeGus e1f9251186 Final updates (I hope for real this time) 2024-12-10 21:03:50 -06:00
LukeGus b60924b9c7 Merge remote-tracking branch 'origin/release-1.0' into release-1.0 2024-12-08 15:22:54 -06:00
LukeGus bd844c7f11 Update name (need to fix logo) 2024-12-08 15:22:39 -06:00
Karmaa ec64bb401b Update README.md 2024-12-08 15:10:07 -06:00
LukeGus 963ac01671 Final changes to release-1.0 2024-12-08 15:09:38 -06:00
Karmaa 50c07c2f3a Update README.md 2024-12-08 15:06:47 -06:00
LukeGus 202de2123b Update image name 2024-12-08 15:06:20 -06:00
LukeGus f677a54551 Add images 2024-12-08 15:02:04 -06:00
Karmaa 0554f9c0fc Update README.md 2024-12-08 14:55:27 -06:00
Karmaa 1901c09a70 Update README.md #7 2024-12-08 14:45:54 -06:00
Karmaa 8229048e4c Update README.md #6 2024-12-08 14:20:20 -06:00
Karmaa d7ca5eccf3 Update README.md #5 2024-12-08 14:17:30 -06:00
LukeGus 1adb64524c Update README.md #4 2024-12-08 14:14:28 -06:00
LukeGus 56ed93bb88 Update README.md #3 2024-12-08 01:50:40 -06:00
LukeGus 861194d8d1 Update README.md #2 2024-12-08 01:48:43 -06:00
LukeGus 74ce228fe1 Update README.md #1 (also added MIT License) 2024-12-08 01:42:43 -06:00
LukeGus 17d2e65187 Change project name, create logo, change README.md to be soon updated. 2024-12-08 01:24:14 -06:00
LukeGus 376fbb1b66 Remove files ignored by .gitignore 2024-12-07 17:30:55 -06:00
LukeGus e68a508dec Repo clean-up 2024-12-07 14:46:20 -06:00
LukeGus ad7532e88d Release 1.0!!!! 2024-12-07 14:35:56 -06:00
LukeGus c2405b0df2 Docker build update #10 (actual final) 2024-12-07 02:09:10 -06:00
LukeGus d422a9ad25 Docker build update #10 (I hope final) 2024-12-07 02:06:19 -06:00
LukeGus ebf00630fd Docker build update #10 2024-12-07 01:59:05 -06:00
LukeGus 0bb4f2fb0b Docker build update #9 (nevermind not finanl) 2024-12-07 01:55:36 -06:00
LukeGus bc49d646b3 Docker build update #8 (nevermind not finanl) 2024-12-07 01:49:32 -06:00
LukeGus 0f764dfb98 Docker build update #7 (final) 2024-12-07 01:45:55 -06:00
LukeGus 2fa968662e Merge remote-tracking branch 'origin/release-1.0' into release-1.0
# Conflicts:
#	.github/workflows/docker-image.yml
#	.idea/workspace.xml
2024-12-07 01:42:35 -06:00
LukeGus b5da3994b0 Docker build update #6 (cache and cleanup) 2024-12-07 01:42:16 -06:00
LukeGus 1820f36155 Docker build update #5 (cache and cleanup) 2024-12-07 01:35:16 -06:00
LukeGus 87107b8cc2 Docker build update #4 (cache and cleanup) 2024-12-07 01:31:37 -06:00
LukeGus 0cfde2410e Docker build update #3 2024-12-07 01:27:06 -06:00
LukeGus 25f8ce74ea Docker build update #2 2024-12-07 01:24:27 -06:00
LukeGus 02acc72bf6 Docker build update 2024-12-07 01:18:52 -06:00
LukeGus a81b4834ce Silent resize cmds & Auto resize terminal #2 2024-12-07 00:44:59 -06:00
LukeGus a2a441cdca Silent resize cmds & Auto resize terminal #1 2024-12-07 00:32:06 -06:00
LukeGus 4b927decce Test ntfy build notification system 2024-12-06 20:42:59 -06:00
LukeGus bda3fe9b31 Terminal size fix #18 (rows & cols fix) 2024-12-06 20:21:14 -06:00
LukeGus fe09966196 Full return back to old code with minor fixes 2024-12-06 18:54:54 -06:00
LukeGus bc14f803a2 Nano zoom fix #17 (ssh-2-promise port fix) 2024-12-05 20:30:58 -06:00
LukeGus b3e43a45af Nano zoom fix #16 (ssh-2-promise) 2024-12-05 20:25:23 -06:00
LukeGus 95573f7e37 Nano zoom fix #15 2024-12-05 20:04:38 -06:00
LukeGus 0ed7d7a083 Nano zoom fix #14 2024-12-05 19:58:24 -06:00
LukeGus 474965835f Nano zoom fix #13 2024-12-05 19:49:18 -06:00
LukeGus 1f75dc0bd2 Nano zoom fix #12 2024-12-05 19:42:08 -06:00
LukeGus 7b545367dc Nano zoom fix #11 2024-12-05 19:34:17 -06:00
LukeGus 1a0d224ae0 Nano zoom fix #11 2024-12-05 19:19:04 -06:00
LukeGus 848b388734 Nano zoom fix #10 2024-12-04 22:07:51 -06:00
LukeGus ea2343551d Nano zoom fix #9 2024-12-04 22:04:01 -06:00
LukeGus d46ddfe6ee Nano and timeout error fix #8 (zoom method) 2024-12-04 21:58:40 -06:00
LukeGus a407bf15f7 Nano and timeout error fix #7 (plus css changes for hide buttons) 2024-12-04 21:53:21 -06:00
LukeGus 3721edc128 Nano and timeout error fix #6 2024-12-04 21:48:06 -06:00
LukeGus 0900017218 Nano and timeout error fix #5 2024-12-04 21:43:38 -06:00
LukeGus f14db79784 Nano and timeout error fix #4 2024-12-04 21:34:56 -06:00
LukeGus 870cd9245e Nano and timeout error fix #3 2024-12-04 21:34:37 -06:00
LukeGus 246bb8fb26 Nano and timeout error fix #2 2024-12-04 21:30:37 -06:00
LukeGus 1139160e48 Nano and timeout error fix #1 2024-12-04 21:19:30 -06:00
LukeGus b6a3f881a8 Initial Commit 2024-12-04 21:04:46 -06:00
1884 changed files with 9983 additions and 1975977 deletions

No files matched your search

-6
View File
@@ -1,6 +0,0 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"subject-case": [0]
}
}
-68
View File
@@ -1,68 +0,0 @@
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
dist
build
coverage
release
# Local data, secrets and editor state
db
old_db
ssl
uploads
plugins
.env
.env.*
.mcp.json
.claude
CLAUDE.md
termix-plugin-signing.key
*.tmxplug.sig
.vscode
.idea
*.swp
*.swo
*~
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
.git
.gitignore
.husky
README.md
CONTRIBUTING.md
CHANGELOG.md
LICENSE
electron/
electron-builder.json
Casks
packaging
*.log
*.tmp
*.temp
logs
pids
*.pid
*.seed
*.pid.lock
.nyc_output
.npm
.eslintcache
.cache
.parcel-cache
*.tgz
.node_repl_history
-17
View File
@@ -1,17 +0,0 @@
* text=auto eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.icns binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
-75
View File
@@ -1,75 +0,0 @@
name: Beta feedback
description: A bug, rough edge or idea from a Termix beta
labels: [beta]
body:
- type: markdown
attributes:
value: |
Thanks for testing a beta. Short reports are fine. If you opened this from Termix, the version and setup are already filled in.
- type: input
id: termix-version
attributes:
label: Beta version
description: Shown in Settings under Updates, or your image tag.
placeholder: "e.g. 26.11.0-beta.2"
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: The bug, what felt off, or your idea. Screenshots help a lot.
validations:
required: true
- type: dropdown
id: regression
attributes:
label: Does this happen on the latest stable release?
options:
- "Not tested on stable"
- "Yes, it happens on stable too"
- "No, only on this beta"
- "Not a bug, just feedback"
validations:
required: false
- type: textarea
id: steps
attributes:
label: Steps to reproduce
description: As few steps as you can.
placeholder: |
1.
2.
3.
validations:
required: false
- type: textarea
id: beta-plugins
attributes:
label: Plugins on a beta
description: Filled in by Termix. One plugin and version per line.
render: text
validations:
required: false
- type: textarea
id: environment
attributes:
label: Environment
description: Browser or desktop app, OS, and how the server is installed.
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs
description: Errors from the browser console, `docker logs termix`, or the error Termix shows. This is shown as code, so no backticks needed.
render: text
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Before you submit
options:
- label: I searched open issues and this is not a duplicate
required: true
-107
View File
@@ -1,107 +0,0 @@
name: Bug report
description: Something in Termix itself is broken
labels: [bug]
type: Bug
body:
- type: markdown
attributes:
value: |
Thanks for the report. A maintainer reads every issue, even when there is no reply right away. We usually reply once there is a fix or a workaround.
If you came from **Report an issue** in Termix, your versions and environment are already filled in below.
Is it about a plugin (terminal, file manager, Docker, remote desktop and so on)? Report it on that plugin's repo instead. **Report an issue** on the plugin's page in Termix takes you there with your versions filled in.
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Describe the bug. Screenshots help a lot.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
description: As few steps as you can.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect?
description: What should have happened instead?
validations:
required: false
- type: input
id: termix-version
attributes:
label: Termix version
description: Shown in Settings under About, or in your User Profile.
placeholder: "e.g. 26.10.0"
validations:
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: Where do you see the problem?
options:
- "Web browser"
- "Desktop app (Windows)"
- "Desktop app (macOS)"
- "Desktop app (Linux)"
- "Mobile app (iOS)"
- "Mobile app (Android)"
- "Other"
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: Server install
description: How is the Termix server installed?
options:
- "Docker"
- "Manual build"
- "Proxmox community script"
- "Desktop app (built-in server)"
- "Not sure"
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Browser and OS, plus anything special about your setup (reverse proxy, database, SSO).
placeholder: |
Web, Chrome on Windows. Behind nginx, Postgres.
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs
description: Errors from the browser console, `docker logs termix`, or the error Termix shows. This is shown as code, so no backticks needed.
render: text
validations:
required: false
- type: textarea
id: context
attributes:
label: Anything else?
description: Links, config, or anything that might help.
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Before you submit
options:
- label: I searched open and closed issues and this is not a duplicate
required: true
- label: I am on the latest version, or I said which version I am on
required: true
-11
View File
@@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Problem with a plugin?
url: https://github.com/Termix-SSH/Termix#plugins
about: Each plugin has its own repo. Find it in this list, or use Report an issue on the plugin's page in Termix.
- name: Questions and chat
url: https://discord.gg/jVQGdvHDrf
about: Ask the community on Discord. Bugs and ideas belong in issues.
- name: Business or priority support
url: https://termix.site/contact
about: For teams that need a faster response. Contact us to discuss terms.
@@ -1,41 +0,0 @@
name: Feature request
description: Suggest an idea for Termix itself
labels: [feature]
type: Feature
body:
- type: markdown
attributes:
value: |
Is it about a plugin (terminal, file manager, Docker, remote desktop and so on)? Report it on that plugin's repo instead. **Report an issue** on the plugin's page in Termix takes you there with your versions filled in.
- type: markdown
attributes:
value: |
Thanks for the idea. Explain the problem first, then how you would solve it.
- type: textarea
id: problem
attributes:
label: What problem would this solve?
description: What are you trying to do, and what gets in the way today?
validations:
required: true
- type: textarea
id: solution
attributes:
label: What would you like?
description: Describe how it should work.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: Anything you tried or considered instead.
validations:
required: false
- type: textarea
id: context
attributes:
label: Anything else?
description: Screenshots, mockups or links.
validations:
required: false
+81
View File
@@ -0,0 +1,81 @@
name: Build and Push Docker Image
on:
push:
branches:
- development
workflow_dispatch:
inputs:
tag_name:
description: "Custom tag name for the Docker image"
required: false
default: ""
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
- name: Install Dependencies and Build Frontend
run: |
cd src
npm ci
npm run build
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Determine Docker image tag
run: |
echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
if [ "${{ github.event.inputs.tag_name }}" == "" ]; then
IMAGE_TAG="${{ github.ref_name }}-development-latest"
else
IMAGE_TAG="${{ github.event.inputs.tag_name }}"
fi
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
- name: Build and Push Multi-Arch Docker Image
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/${{ env.REPO_OWNER }}/termix:${{ env.IMAGE_TAG }}
labels: org.opencontainers.image.source=https://github.com/${{ github.repository }}
- name: Notify via ntfy
run: |
curl -d "Docker image build and push completed successfully for tag: ${{ env.IMAGE_TAG }}" \
https://ntfy.karmaa.site/termix-build
- name: Delete all untagged image versions
uses: quartx-analytics/ghcr-cleaner@v1
with:
owner-type: user
token: ${{ secrets.GHCR_TOKEN }}
repository-owner: ${{ github.repository_owner }}
delete-untagged: true
- name: Cleanup Docker Images Locally
run: |
docker image prune -af
docker system prune -af --volumes
-157
View File
@@ -1,157 +0,0 @@
name: Build and Push Docker Image
on:
workflow_dispatch:
inputs:
version:
description: "Version to build (e.g., 26.10.0)"
required: true
build_type:
description: "Build type"
required: true
default: "Development"
type: choice
options:
- Development
- Beta
- Production
source_ref:
description: "Git ref/SHA to build (defaults to the workflow ref)"
required: false
default: ""
workflow_call:
inputs:
version:
description: "Version to build (e.g., 26.10.0)"
required: true
type: string
build_type:
description: "Build type (Development, Beta or Production)"
required: true
type: string
dry_run:
description: "Build the image but do not push to any registry"
required: false
type: boolean
default: false
source_ref:
description: "Git ref/SHA to build"
required: false
type: string
default: ""
jobs:
build:
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
ref: ${{ inputs.source_ref || github.ref }}
fetch-depth: 1
- name: Resolve source revision
run: |
echo "SOURCE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
echo "BUILD_CREATED=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
# The image reads its version from package.json, so a beta has to say so.
- name: Stamp beta version
if: ${{ inputs.build_type == 'Beta' }}
run: node scripts/sync-version.cjs --version "${{ inputs.version }}"
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
with:
platforms: linux/amd64,linux/arm64
- name: Setup Docker Buildx
uses: useblacksmith/setup-docker-builder@v2
with:
cache-key: termix-ssh/termix
- name: Determine tags
id: tags
run: |
VERSION=${{ inputs.version }}
BUILD_TYPE=${{ inputs.build_type }}
TAGS=()
ALL_TAGS=()
if [ "$BUILD_TYPE" = "Production" ]; then
TAGS+=("$VERSION" "latest")
elif [ "$BUILD_TYPE" = "Beta" ]; then
TAGS+=("beta" "$VERSION")
else
TAGS+=("dev-$VERSION")
fi
for tag in "${TAGS[@]}"; do
ALL_TAGS+=("ghcr.io/lukegus/termix:$tag")
ALL_TAGS+=("ghcr.io/termix-ssh/termix:$tag")
if [ "$BUILD_TYPE" != "Development" ]; then
ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag")
fi
done
echo "ALL_TAGS=$(IFS=,; echo "${ALL_TAGS[*]}")" >> $GITHUB_ENV
echo "TAGS=${TAGS[*]}" >> $GITHUB_ENV
- name: Login to GHCR
if: ${{ !inputs.dry_run }}
uses: docker/login-action@v4
with:
registry: ghcr.io
username: lukegus
password: ${{ secrets.TERMIX_PAT }}
- name: Login to Docker Hub (prod and beta only)
if: ${{ (inputs.build_type == 'Production' || inputs.build_type == 'Beta') && !inputs.dry_run }}
uses: docker/login-action@v4
with:
username: bugattiguy527
password: ${{ secrets.DOCKERHUB_BUGATTIGUY527_TOKEN }}
- name: Build and push multi-arch image
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ./docker/Dockerfile
push: ${{ !inputs.dry_run }}
platforms: linux/amd64,linux/arm64
tags: ${{ env.ALL_TAGS }}
build-args: |
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
labels: |
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.revision=${{ env.SOURCE_SHA }}
org.opencontainers.image.created=${{ env.BUILD_CREATED }}
outputs: ${{ inputs.dry_run && 'type=cacheonly' || 'type=registry,compression=zstd' }}
- name: Login to Docker Hub as termixssh
id: termix_hub
if: ${{ (inputs.build_type == 'Production' || inputs.build_type == 'Beta') && !inputs.dry_run }}
env:
TOKEN: ${{ secrets.DOCKERHUB_TERMIXSSH_TOKEN }}
run: |
if [ -z "$TOKEN" ]; then
echo "DOCKERHUB_TERMIXSSH_TOKEN is not set, skipping docker.io/termixssh"
exit 0
fi
echo "$TOKEN" | docker login docker.io --username termixssh --password-stdin
echo "ready=true" >> "$GITHUB_OUTPUT"
- name: Copy image to docker.io/termixssh
if: ${{ steps.termix_hub.outputs.ready == 'true' }}
run: |
for tag in $TAGS; do
docker buildx imagetools create --tag "docker.io/termixssh/termix:$tag" "ghcr.io/termix-ssh/termix:$tag"
done
- name: Cleanup Docker
if: always()
run: |
docker image prune -af
docker system prune -af --volumes
File diff suppressed because it is too large. Load diff
-120
View File
@@ -1,120 +0,0 @@
name: PR Check
on:
pull_request:
branches: [main, dev-*]
jobs:
lint-and-build:
runs-on: blacksmith-2vcpu-ubuntu-2404
env:
NODE_OPTIONS: "--max-old-space-size=4096"
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: Install dependencies
run: npm ci
# Bundled plugins are counted as downloads in the plugin store, so a
# build only fetches them when the pins change.
- name: Cache bundled plugins
uses: actions/cache@v6
with:
path: node_modules/.cache/termix-bundled-plugins
key: bundled-plugins-${{ hashFiles('docker/bundled-plugins.json') }}
enableCrossOsArchive: true
- name: Build plugin SDK
run: npm run build:sdk
- name: Lint
run: npm run lint
- name: Run Prettier check
run: npx prettier --check .
- name: Type check
run: npm run type-check
- name: Build
run: npm run build
- name: Run unit tests
run: npm run test
database-dialects:
name: Postgres and MySQL
runs-on: blacksmith-2vcpu-ubuntu-2404
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: termix
POSTGRES_PASSWORD: termix
POSTGRES_DB: termix_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: termix
MYSQL_DATABASE: termix_test
MYSQL_USER: termix
MYSQL_PASSWORD: termix
ports:
- 3306:3306
options: >-
--health-cmd "mysqladmin ping -h 127.0.0.1 -ptermix"
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build plugin SDK
run: npm run build:sdk
- name: Verify Postgres
run: npm run verify:dialect -- postgres://termix:termix@127.0.0.1:5432/termix_test
- name: Verify MySQL
run: npm run verify:dialect -- mysql://termix:termix@127.0.0.1:3306/termix_test
- name: Repository tests on Postgres
env:
TEST_DIALECT: postgres
TEST_DATABASE_URL: postgres://termix:termix@127.0.0.1:5432/termix_test
run: npx vitest run src/backend/tests/database/repositories --no-file-parallelism
- name: Repository tests on MySQL
env:
TEST_DIALECT: mysql
TEST_DATABASE_URL: mysql://termix:termix@127.0.0.1:3306/termix_test
run: npx vitest run src/backend/tests/database/repositories --no-file-parallelism
-84
View File
@@ -1,84 +0,0 @@
name: Publish SDK
on:
workflow_dispatch:
inputs:
mode:
description: "Publish mode"
required: true
default: Publish
type: choice
options:
- Publish
- Dry run
permissions:
contents: write
id-token: write
jobs:
publish:
name: Publish @termix-ssh/plugin-sdk
runs-on: ubuntu-latest
env:
NODE_OPTIONS: "--max-old-space-size=4096"
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
cache: npm
registry-url: https://registry.npmjs.org
- name: Update npm
run: npm install -g npm@^11.19.0
- run: npm ci
- name: Check the version is new
run: |
VERSION=$(node -p "require('./packages/plugin-sdk/package.json').version")
if npm view "@termix-ssh/plugin-sdk@$VERSION" version >/dev/null 2>&1; then
echo "@termix-ssh/plugin-sdk@$VERSION is already on npm. Bump packages/plugin-sdk/package.json first."
if [ "${{ inputs.mode }}" = "Publish" ]; then
exit 1
fi
fi
echo "Publishing @termix-ssh/plugin-sdk@$VERSION"
- run: npm run build:sdk
- run: npm run lint
- run: npm run type-check
- run: npm run test
- name: Publish
run: |
if [ "${{ inputs.mode }}" = "Dry run" ]; then
npm publish --workspace @termix-ssh/plugin-sdk --provenance --access public --dry-run
else
npm publish --workspace @termix-ssh/plugin-sdk --provenance --access public
fi
- name: Tag and release
if: ${{ inputs.mode == 'Publish' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION=$(node -p "require('./packages/plugin-sdk/package.json').version")
node -e "
const fs = require('fs');
const { changelogSection } = require('./packages/plugin-sdk/dist/changelog.js');
const notes = changelogSection(fs.readFileSync('packages/plugin-sdk/CHANGELOG.md', 'utf8'), process.argv[1]);
fs.writeFileSync('sdk-notes.md', notes);
" "$VERSION"
gh release create "sdk-v$VERSION" \
--target "${{ github.sha }}" \
--title "Plugin SDK v$VERSION" \
--notes-file sdk-notes.md \
--latest=false
-674
View File
@@ -1,674 +0,0 @@
name: Release
# Stable: run by hand from a dev-X.Y.Z branch. Beta: run by hand from a dev
# branch, or let the Tuesday and Friday schedule snapshot the newest dev
# branch when it moved since the last beta.
on:
schedule:
- cron: "0 15 * * 2,5"
workflow_dispatch:
inputs:
channel:
description: "Stable release or beta"
required: true
default: Stable
type: choice
options:
- Stable
- Beta
mode:
description: "Release mode (Overwrite release is stable only)"
required: true
default: Everything
type: choice
options:
- Everything
- Overwrite release
- Dry run
- Skip submit
permissions:
contents: write
concurrency:
group: release
cancel-in-progress: false
jobs:
prep:
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
channel: ${{ steps.mode.outputs.channel }}
mode: ${{ steps.mode.outputs.mode }}
skip: ${{ steps.beta.outputs.skip || 'false' }}
version: ${{ steps.info.outputs.version || steps.beta.outputs.version }}
release_tag: ${{ steps.info.outputs.release_tag || steps.beta.outputs.release_tag }}
mobile_version: ${{ steps.info.outputs.mobile_version }}
mobile_tag: ${{ steps.info.outputs.mobile_tag }}
dev_branch: ${{ steps.info.outputs.dev_branch || steps.beta.outputs.dev_branch }}
sha: ${{ steps.beta.outputs.sha }}
prev_ref: ${{ steps.beta.outputs.prev_ref }}
steps:
- name: Resolve channel and mode
id: mode
run: |
CHANNEL="${{ inputs.channel || 'Beta' }}"
MODE="${{ inputs.mode || 'Everything' }}"
echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT"
echo "mode=$MODE" >> "$GITHUB_OUTPUT"
if [ "$CHANNEL" = "Beta" ] && [ "$MODE" = "Overwrite release" ]; then
echo "Overwrite release is for stable releases only."
exit 1
fi
- name: Guard branch (release modes)
if: ${{ github.event_name == 'workflow_dispatch' && steps.mode.outputs.mode != 'Overwrite release' && !startsWith(github.ref, 'refs/heads/dev-') }}
run: |
echo "This mode must be run from a dev branch (got ${{ github.ref }})."
exit 1
- name: Guard branch (overwrite mode)
if: ${{ steps.mode.outputs.mode == 'Overwrite release' && github.ref != 'refs/heads/main' }}
run: |
echo "Overwrite release must be run from main (got ${{ github.ref }})."
exit 1
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Resolve versions
id: info
if: ${{ steps.mode.outputs.channel == 'Stable' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ steps.mode.outputs.mode }}" = "Overwrite release" ]; then
DEV_BRANCH=""
VERSION=$(node -p "require('./package.json').version")
else
DEV_BRANCH="${GITHUB_REF#refs/heads/}"
VERSION=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH")
fi
echo "dev_branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "release_tag=v$VERSION" >> "$GITHUB_OUTPUT"
MOBILE_RAW=$(gh release view -R Termix-SSH/Mobile --json tagName -q .tagName)
MOBILE_VERSION=$(echo "$MOBILE_RAW" | sed -E 's/^release-//; s/-tag$//; s/^v//')
if [ -z "$MOBILE_VERSION" ]; then
echo "Failed to resolve the latest Termix-SSH/Mobile release version."
exit 1
fi
echo "mobile_version=$MOBILE_VERSION" >> "$GITHUB_OUTPUT"
echo "mobile_tag=$MOBILE_RAW" >> "$GITHUB_OUTPUT"
- name: Check CHANGELOG.md has notes for this version
if: ${{ steps.mode.outputs.channel == 'Stable' }}
run: |
node scripts/generate-release-body.cjs \
--version "${{ steps.info.outputs.version }}" \
--mobile-version "${{ steps.info.outputs.mobile_version }}" \
--mobile-tag "${{ steps.info.outputs.mobile_tag }}" > /dev/null
- name: Resolve beta version
id: beta
if: ${{ steps.mode.outputs.channel == 'Beta' }}
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
if [ "${{ github.event_name }}" = "schedule" ]; then
REFS=$(gh api "repos/${{ github.repository }}/branches" --paginate -q '.[].name')
if ! DEV_BRANCH=$(printf '%s\n' "$REFS" | node scripts/latest-dev-branch.cjs 2>/dev/null); then
echo "No dev-X.Y.Z branch is open, so no beta today."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
else
DEV_BRANCH="${GITHUB_REF#refs/heads/}"
fi
BASE=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH")
SHA=$(gh api "repos/${{ github.repository }}/branches/$DEV_BRANCH" -q .commit.sha)
TAGS=$(gh api "repos/${{ github.repository }}/git/matching-refs/tags/v$BASE-beta." -q '.[].ref' | sed 's#^refs/tags/##')
LATEST=$(printf '%s\n' "$TAGS" | node scripts/beta-version.cjs "$BASE" --latest)
VERSION=$(printf '%s\n' "$TAGS" | node scripts/beta-version.cjs "$BASE")
if [ -n "$LATEST" ]; then
LATEST_SHA=$(gh api "repos/${{ github.repository }}/commits/$LATEST" -q .sha)
if [ "$LATEST_SHA" = "$SHA" ] && [ "${{ github.event_name }}" = "schedule" ]; then
echo "$DEV_BRANCH has not moved since $LATEST, so no beta today."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
PREV="$LATEST"
else
PREV=$(gh release view --repo ${{ github.repository }} --json tagName -q .tagName 2>/dev/null || true)
fi
echo "Beta $VERSION from $DEV_BRANCH @ $SHA, changes since ${PREV:-nothing}"
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "dev_branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "release_tag=v$VERSION" >> "$GITHUB_OUTPUT"
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "prev_ref=$PREV" >> "$GITHUB_OUTPUT"
# Translates new or changed strings with the Termix LLM server and commits
# them to the dev branch, so the beta or stable build includes them. If the
# server is down it warns and the release goes on with what is there.
translate:
needs: [prep]
if: ${{ needs.prep.outputs.skip != 'true' && needs.prep.outputs.mode != 'Overwrite release' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
sha: ${{ steps.commit.outputs.sha }}
steps:
- name: Checkout dev branch
uses: actions/checkout@v7
with:
ref: ${{ needs.prep.outputs.sha || needs.prep.outputs.dev_branch }}
fetch-depth: 1
token: ${{ secrets.TERMIX_PAT }}
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Translate
uses: Termix-SSH/Termix-Registry/translate@main
with:
source: src/ui/locales/en.json
out: src/ui/locales/translated
languages: src/ui/locales/languages.json
api-key: ${{ secrets.TERMIX_LLM_API_KEY }}
- name: Commit translations
id: commit
run: |
git add src/ui/locales
if git diff --cached --quiet; then
echo "No translation changes."
elif [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
echo "DRY RUN: would commit these translation changes:"
git --no-pager diff --cached --stat
else
git config user.name "LukeGus"
git config user.email "bugattiguy527@gmail.com"
git commit -m "chore: update translations"
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
fi
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
verify-beta:
needs: [prep, translate]
if: ${{ needs.prep.outputs.channel == 'Beta' && needs.prep.outputs.skip != 'true' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout beta commit
uses: actions/checkout@v7
with:
ref: ${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: Install dependencies
run: npm ci
# Bundled plugins are counted as downloads in the plugin store, so a
# build only fetches them when the pins change.
- name: Cache bundled plugins
uses: actions/cache@v6
with:
path: node_modules/.cache/termix-bundled-plugins
key: bundled-plugins-${{ hashFiles('docker/bundled-plugins.json') }}
enableCrossOsArchive: true
- name: Build plugin SDK
run: npm run build:sdk
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Build
run: npm run build
- name: Run unit tests
run: npm run test
normalize:
needs: [prep, translate]
if: ${{ needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Overwrite release' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout dev branch
uses: actions/checkout@v7
with:
ref: ${{ needs.prep.outputs.dev_branch }}
fetch-depth: 0
token: ${{ secrets.TERMIX_PAT }}
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
cache: "npm"
- name: Install dependencies
run: npm ci
# Manifest validation in lint loads the built SDK.
- name: Build SDK
run: npm run build:sdk
- name: Lint and format
run: |
npm run lint:fix
npm run format
npm run lint
- name: Run unit tests
run: npm run test
- name: Sync version
run: node scripts/sync-version.cjs --version "${{ needs.prep.outputs.version }}"
- name: Commit changes to dev branch
run: |
git config user.name "LukeGus"
git config user.email "bugattiguy527@gmail.com"
if git diff --quiet; then
echo "No lint/format/version changes to commit."
exit 0
fi
if [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
echo "DRY RUN: would commit and push the following changes to ${{ needs.prep.outputs.dev_branch }}:"
git --no-pager diff --stat
exit 0
fi
git add -A
git commit -m "chore: lint, format, and bump version to ${{ needs.prep.outputs.version }}"
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
sync-main:
needs: [prep, normalize]
if: ${{ needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Overwrite release' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout dev branch
uses: actions/checkout@v7
with:
ref: ${{ needs.prep.outputs.dev_branch }}
fetch-depth: 0
token: ${{ secrets.TERMIX_PAT }}
- name: Merge main into dev branch
run: |
git config user.name "LukeGus"
git config user.email "bugattiguy527@gmail.com"
git fetch origin main
# -X ours leaves modify/delete conflicts, so settle those to the dev side too
if ! git merge origin/main -X ours --no-edit; then
git diff --name-only --diff-filter=U | while read -r f; do
if git cat-file -e "HEAD:$f" 2>/dev/null; then
git checkout --ours -- "$f" && git add -- "$f"
else
git rm -q -- "$f"
fi
done
git commit --no-edit
fi
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
merge-to-main:
needs: [prep, translate, normalize, sync-main]
if: ${{ always() && needs.prep.result == 'success' && needs.prep.outputs.channel == 'Stable' && (needs.translate.result == 'success' || needs.translate.result == 'skipped') && (needs.normalize.result == 'success' || needs.normalize.result == 'skipped') && (needs.sync-main.result == 'success' || needs.sync-main.result == 'skipped') }}
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
main_sha: ${{ steps.merge.outputs.main_sha }}
build_ref: ${{ steps.merge.outputs.build_ref }}
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Generate PR body
id: body
run: |
node scripts/generate-release-body.cjs \
--version "${{ needs.prep.outputs.version }}" \
--mobile-version "${{ needs.prep.outputs.mobile_version }}" \
--mobile-tag "${{ needs.prep.outputs.mobile_tag }}" > PR_BODY.md
- name: Open and squash-merge PR to main
id: merge
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
DEV_BRANCH="${{ needs.prep.outputs.dev_branch }}"
TITLE="v${{ needs.prep.outputs.version }}"
if [ "${{ needs.prep.outputs.mode }}" = "Overwrite release" ]; then
echo "OVERWRITE: no merge; building from main as-is."
echo "build_ref=main" >> "$GITHUB_OUTPUT"
echo "main_sha=$(gh api repos/${{ github.repository }}/commits/main -q .sha)" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${{ needs.prep.outputs.mode }}" = "Dry run" ]; then
echo "DRY RUN: would open and squash-merge a PR from $DEV_BRANCH into main."
echo "DRY RUN: downstream jobs will build from $DEV_BRANCH instead of main."
echo "build_ref=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
echo "main_sha=" >> "$GITHUB_OUTPUT"
exit 0
fi
PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head "$DEV_BRANCH" --base main --state open --json number -q '.[0].number' || true)
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh pr create --repo ${{ github.repository }} \
--base main --head "$DEV_BRANCH" \
--title "$TITLE" --body-file PR_BODY.md \
| grep -oE '[0-9]+$')
fi
gh pr merge "$PR_NUMBER" --repo ${{ github.repository }} --squash --admin
STATE=$(gh pr view "$PR_NUMBER" --repo ${{ github.repository }} --json state -q .state)
if [ "$STATE" != "MERGED" ]; then
echo "PR #$PR_NUMBER did not merge (state: $STATE)."
exit 1
fi
MAIN_SHA=$(gh api repos/${{ github.repository }}/commits/main -q .sha)
echo "main_sha=$MAIN_SHA" >> "$GITHUB_OUTPUT"
echo "build_ref=$MAIN_SHA" >> "$GITHUB_OUTPUT"
docker:
needs: [prep, translate, verify-beta, merge-to-main]
if: ${{ always() && needs.prep.result == 'success' && needs.prep.outputs.skip != 'true' && (needs.merge-to-main.result == 'success' || needs.verify-beta.result == 'success') }}
uses: ./.github/workflows/docker.yml
with:
version: ${{ needs.prep.outputs.version }}
build_type: ${{ needs.prep.outputs.channel == 'Beta' && 'Beta' || 'Production' }}
dry_run: ${{ needs.prep.outputs.mode == 'Dry run' }}
source_ref: ${{ needs.merge-to-main.outputs.build_ref || needs.translate.outputs.sha || needs.prep.outputs.sha }}
secrets: inherit
create-release:
needs: [prep, merge-to-main, docker]
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.docker.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v7
with:
ref: main
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Clear existing release artifacts (overwrite mode)
if: ${{ needs.prep.outputs.mode == 'Overwrite release' }}
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
TAG="${{ needs.prep.outputs.release_tag }}"
if ! gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
echo "Overwrite release: no existing release $TAG to overwrite."
exit 1
fi
echo "Clearing existing assets from $TAG..."
gh release view "$TAG" --repo ${{ github.repository }} --json assets -q '.assets[].name' | while read -r ASSET; do
[ -n "$ASSET" ] && gh release delete-asset "$TAG" "$ASSET" --repo ${{ github.repository }} --yes || true
done
- name: Generate release body
if: ${{ needs.prep.outputs.mode != 'Overwrite release' }}
run: |
node scripts/generate-release-body.cjs \
--version "${{ needs.prep.outputs.version }}" \
--mobile-version "${{ needs.prep.outputs.mobile_version }}" \
--mobile-tag "${{ needs.prep.outputs.mobile_tag }}" > RELEASE_BODY.md
- name: Create or update GitHub release
if: ${{ needs.prep.outputs.mode != 'Overwrite release' }}
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
TAG="${{ needs.prep.outputs.release_tag }}"
TITLE="Termix v${{ needs.prep.outputs.version }}"
if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
gh release edit "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md
else
gh release create "$TAG" --repo ${{ github.repository }} --title "$TITLE" --notes-file RELEASE_BODY.md --target "${{ needs.merge-to-main.outputs.main_sha }}"
fi
create-beta-release:
needs: [prep, translate, docker]
if: ${{ always() && needs.prep.outputs.channel == 'Beta' && needs.docker.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
permissions:
contents: write
steps:
- name: Checkout beta commit
uses: actions/checkout@v7
with:
ref: ${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Generate beta release body
run: |
node scripts/generate-beta-body.cjs \
--version "${{ needs.prep.outputs.version }}" \
--branch "${{ needs.prep.outputs.dev_branch }}" \
--sha "${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}" \
--prev "${{ needs.prep.outputs.prev_ref }}" > BETA_BODY.md
- name: Create beta release
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
VERSION="${{ needs.prep.outputs.version }}"
TITLE="Termix v${VERSION%-beta.*} Beta ${VERSION##*-beta.}"
TAG="${{ needs.prep.outputs.release_tag }}"
if gh release view "$TAG" --repo ${{ github.repository }} >/dev/null 2>&1; then
gh release edit "$TAG" --repo ${{ github.repository }} \
--title "$TITLE" --notes-file BETA_BODY.md --prerelease
else
gh release create "$TAG" --repo ${{ github.repository }} \
--title "$TITLE" --notes-file BETA_BODY.md \
--prerelease --target "${{ needs.translate.outputs.sha || needs.prep.outputs.sha }}"
fi
electron-release:
needs:
[
prep,
translate,
merge-to-main,
docker,
create-release,
create-beta-release,
]
if: ${{ always() && needs.prep.outputs.skip != 'true' && needs.docker.result == 'success' && needs.create-release.result != 'failure' && needs.create-beta-release.result != 'failure' }}
uses: ./.github/workflows/electron.yml
with:
build_type: all
artifact_destination: ${{ needs.prep.outputs.mode == 'Dry run' && 'file' || 'release' }}
release_tag: ${{ needs.prep.outputs.release_tag }}
version_override: ${{ needs.prep.outputs.channel == 'Beta' && needs.prep.outputs.version || '' }}
source_ref: ${{ needs.merge-to-main.outputs.build_ref || needs.translate.outputs.sha || needs.prep.outputs.sha }}
secrets: inherit
electron-submit:
needs: [prep, merge-to-main, electron-release]
if: ${{ always() && needs.electron-release.result == 'success' && needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Skip submit' }}
uses: ./.github/workflows/electron.yml
with:
build_type: all
artifact_destination: submit
source_ref: ${{ needs.merge-to-main.outputs.build_ref }}
secrets: inherit
cask-commit-back:
needs: [prep, electron-release]
if: ${{ always() && needs.electron-release.result == 'success' && needs.prep.outputs.channel == 'Stable' && needs.prep.outputs.mode != 'Dry run' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v7
with:
ref: main
fetch-depth: 1
token: ${{ secrets.TERMIX_PAT }}
- name: Bump and commit Homebrew cask
env:
VERSION: ${{ needs.prep.outputs.version }}
DMG_SHA256: ${{ needs.electron-release.outputs.macos_universal_dmg_sha256 }}
run: |
if [ -z "$DMG_SHA256" ]; then
echo "No universal DMG checksum available (macOS build unsigned or skipped); leaving cask unchanged."
exit 0
fi
git config user.name "LukeGus"
git config user.email "bugattiguy527@gmail.com"
git fetch origin main
git checkout -B main origin/main
sed -i "s|version \".*\"|version \"$VERSION\"|g" Casks/termix.rb
sed -i "s|sha256 \".*\"|sha256 \"$DMG_SHA256\"|g" Casks/termix.rb
git add Casks/termix.rb
if git diff --cached --quiet; then
echo "Cask already up to date."
exit 0
fi
git commit -m "chore: bump Homebrew cask to $VERSION"
git push origin HEAD:main
prune-betas:
needs: [prep, create-release]
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.create-release.result == 'success' && needs.prep.outputs.mode != 'Overwrite release' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Delete this version's beta releases (tags stay)
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
PREFIX="v${{ needs.prep.outputs.version }}-beta."
gh release list --repo ${{ github.repository }} --limit 200 --json tagName -q '.[].tagName' \
| grep -F "$PREFIX" | while read -r TAG; do
echo "Deleting release $TAG"
gh release delete "$TAG" --repo ${{ github.repository }} --yes || true
done
# The docs site reads the env catalog, API spec and download list from the
# newest stable release.
# Docs written for this release live on dev-X.Y.Z in the Docs repo. Squash
# merge it into main once the release is out. No branch means nothing to do.
docs-merge:
needs: [prep, electron-release]
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.electron-release.result == 'success' && needs.prep.outputs.mode != 'Dry run' && needs.prep.outputs.mode != 'Overwrite release' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Merge the docs dev branch into main
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
REPO: Termix-SSH/Docs
VERSION: ${{ needs.prep.outputs.version }}
run: |
BRANCH="dev-$VERSION"
if ! gh api "repos/$REPO/branches/$BRANCH" > /dev/null 2>&1; then
echo "$REPO has no $BRANCH branch, nothing to merge."
exit 0
fi
AHEAD=$(gh api "repos/$REPO/compare/main...$BRANCH" -q .ahead_by)
if [ "$AHEAD" = "0" ]; then
echo "$BRANCH has nothing main doesn't, deleting it."
gh api -X DELETE "repos/$REPO/git/refs/heads/$BRANCH"
exit 0
fi
PR_NUMBER=$(gh pr list --repo "$REPO" --head "$BRANCH" --base main --state open --json number -q '.[0].number')
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh pr create --repo "$REPO" --base main --head "$BRANCH" \
--title "v$VERSION" --body "Docs for Termix v$VERSION." | grep -oE '[0-9]+$')
fi
gh pr merge "$PR_NUMBER" --repo "$REPO" --squash --admin --delete-branch
STATE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json state -q .state)
if [ "$STATE" != "MERGED" ]; then
echo "Docs PR #$PR_NUMBER did not merge (state: $STATE)."
exit 1
fi
docs-refresh:
needs: [prep, electron-release, docs-merge]
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && needs.electron-release.result == 'success' && needs.prep.outputs.mode != 'Dry run' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Tell the docs site
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: gh api repos/Termix-SSH/Docs/dispatches -f event_type=core-released
cleanup:
needs: [prep, merge-to-main, electron-release, cask-commit-back]
if: ${{ always() && needs.prep.outputs.channel == 'Stable' && (needs.prep.outputs.mode == 'Everything' || needs.prep.outputs.mode == 'Skip submit') && needs.merge-to-main.result == 'success' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
persist-credentials: false
- name: Delete dev branch in Termix
env:
GH_TOKEN: ${{ secrets.TERMIX_PAT }}
run: |
git push https://x-access-token:${{ secrets.TERMIX_PAT }}@github.com/${{ github.repository }}.git \
--delete "${{ needs.prep.outputs.dev_branch }}" || true
-53
View File
@@ -1,53 +0,0 @@
name: Translate
on:
workflow_dispatch:
inputs:
retranslate:
description: "Translate every string again, not only new or changed ones"
type: boolean
default: false
permissions:
contents: write
concurrency:
group: translate-${{ github.ref }}
cancel-in-progress: false
jobs:
translate:
if: ${{ startsWith(github.ref, 'refs/heads/dev-') }}
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
token: ${{ secrets.TERMIX_PAT }}
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version-file: ".nvmrc"
- name: Translate
uses: Termix-SSH/Termix-Registry/translate@main
with:
source: src/ui/locales/en.json
out: src/ui/locales/translated
languages: src/ui/locales/languages.json
retranslate: ${{ inputs.retranslate }}
api-key: ${{ secrets.TERMIX_LLM_API_KEY }}
- name: Commit translations
run: |
git add src/ui/locales
if git diff --cached --quiet; then
echo "No translation changes."
exit 0
fi
git config user.name "LukeGus"
git config user.email "bugattiguy527@gmail.com"
git commit -m "chore: update translations"
git push origin HEAD:"${GITHUB_REF_NAME}"
+143 -26
View File
@@ -1,3 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
@@ -7,41 +8,157 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
src/mcp-server/node_modules
dist
dist-ssr
coverage
*.local
# Plugin build output. termix-plugin build writes it, the packaged server
# gets its copy under dist/plugins.
*.tmxplug
*.tmxplug.sig
termix-plugin-signing.key
/src/**/*.js
.vscode/
.idea/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
Thumbs.db
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/db/
/release/
/.claude/
/ssl/
/uploads/
/nul
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
electron/build-info.cjs
/.mcp.json
/CLAUDE.md
/old_db/
/scripts/auto-fix.mjs
/scripts/auto-fix-blocklist.json
/scripts/auto-fix-state.json
/scripts/auto-fix-report-*.json
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.env.local
.env.development.local
.env.test.local
.env.production.local
.bash_history
.bashrc
.init_done
.profile
.sudo_as_admin_successful
.wget-hsts
.git-credentials
.docker/
.bash_logout
# VSCode Files
.vscode-server/
# Configs
.config/
# .dotnet
.dotnet/
# .local
.local/
/docker/docker-compose.yml
/src/data/
/docker/mongodb/
/docker/docker-compose.yml
-1
View File
@@ -1 +0,0 @@
npx --no -- commitlint --edit "$1"
-3
View File
@@ -1,3 +0,0 @@
# The .cmd shims on Windows add their own paths to each command, so keep
# lint-staged's chunks well under cmd.exe's 8191 character limit.
npx lint-staged --max-arg-length 4000
-1
View File
@@ -1 +0,0 @@
legacy-peer-deps=true
-1
View File
@@ -1 +0,0 @@
24
-14
View File
@@ -1,14 +0,0 @@
build
coverage
dist
node_modules
package-lock.json
dist-ssr
release
db
*.min.js
*.min.css
openapi.json
drizzle/
src/ui/settings/settings-search-index.ts
-9
View File
@@ -1,9 +0,0 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80,
"arrowParens": "always",
"endOfLine": "lf"
}
-100
View File
@@ -1,100 +0,0 @@
# Changelog
## 26.10.2
### Added
- Hide sidebar after opening, in Customize Sidebar, closes the host list once you open a host
- Sort the dashboard's Host Status card by name, IP address or status
### Changed
- The side menu now opens over the page when it expands on hover, so open sessions like remote desktop are not resized each time. Turn on Push content when it expands in Settings to get the old behavior back
### Fixed
- Picking a section lower down in the host editor, like Warpgate, stuttered on the way and sometimes stopped before it got there
- Sorting hosts by IP address put 10.0.0.210 before 10.0.0.3
- The local terminal failed to start in the universal macOS build
- Hosts shared with you never showed up on a linked desktop
- A linked desktop could never turn on an unsigned plugin the server had enabled
- Creating, cloning or deleting hosts failed with "no such table: main.p_vault_profiles" after the Vault plugin was removed
- Opening a user in Settings, Users blocked the other admin sections until you went back to the user list
- A linked desktop could keep showing the same sync conflicts, or delete a local host, when the server could not send its copy
- Usernames are now unique
- Importing user data keeps foreign keys on
- Trusted proxy login checks the address nginx saw, not nginx itself
- The desktop login frame only trusts the server's own origin
- External login URLs and callback ports are checked on the desktop app
- A changed jump host key is no longer accepted without asking
- Synced plugins signed only by a key the server sent are no longer turned on by themselves
- Two connects to the same host at once could overwrite each other's connection
- Indonesian uses the same technical terms everywhere
- When the server was down or still starting, the login page said it could not connect to the database and stopped trying. It now says the server is not responding and continues on its own once the server is back
- nginx answered 404 instead of 502 while the backend was down
- A client tunnel that could not start always said "Endpoint SSH connection closed" on the desktop app instead of the real reason, like the target refusing the connection
- The Docker server crashed and restarted every few hours in ssh2's native crypto on Node 24
## 26.10.1
### Added
- Slovak, Croatian, Slovenian, Lithuanian, Latvian, Estonian and Malay
- The desktop app can update itself. Pick Automatic, Ask first or Never in Settings, Updates. Store and package manager installs keep updating the way they do now
- Community plugins. Turn on the community registry in Plugins, Registries to browse and install plugins made by other people, each version reviewed before it is listed
- Custom plugin registries. Add your own registry by its index URL; its signing key is pinned when you add it
### Changed
- Every language is now fully translated, in Termix and in every plugin, and stays up to date with each release
- Languages with more than two plural forms, like Russian, Polish and Arabic, now use the right one
- The Plugins tab shows how many times each plugin was downloaded, in a Downloads column
- Plugins, Browse hides plugins you already have. Turn on Show installed to see them
### Fixed
- Regional browser languages like `es-MX`, `pt` or `zh-HK` showed English instead of the closest translation
- Counts like "1 keys" or "Moved 1 host(s)" now use the singular
- Plugin updates on the desktop app, synced or installed from the Plugins tab, failed to start with "Cannot find package"
- The desktop app showed plugins that only run on the server as not running and out of date
- Keep the terminal WebSocket open past nginx's 60s default
- Plugin download counts now show in the Plugins tab without waiting for the background check
- Clicking into a saved password, key passphrase or sudo password field and saving without typing erased it, which stopped sudo autofill
- The desktop app now says when its data is from 2.8 or older and needs 2.9 first, instead of only saying the local server could not start
- Client tunnels on the desktop app stopped with "Endpoint SSH connection closed" when a reverse proxy dropped their idle connection
- Typing right after opening a host from the sidebar went to the host search instead of the new session
- SSH config import failed on files that put a tab or `=` between a keyword and its value
- App icons and the favicon have rounded corners again
## 26.10.0
### Added
- Plugins. Every feature beyond hosts, credentials and sharing is now its own plugin with its own repo, and you pick which ones you want
- A Plugins tab to install, update, pin, turn off and remove plugins from the official registry, with release notes and a beta channel per plugin
- Developer mode to install a plugin from a file, and a plugin SDK on npm to build your own
- The community plugin registry is open for submissions. Termix can install community plugins in a later update
- Redesigned UI with a Manage tab for hosts and credentials and a Settings tab with search
- New full screen onboarding where you choose which plugins to start with
- Beta channel for Termix itself in Settings > Updates
- Docs links on every settings page, in the Plugins tab, in plugin settings and in the command palette
- Plugins can link their own docs and list the environment variables they read, shown on their page in the Plugins tab
- Report a bug links that open a prefilled issue in the right repo
- `TRUSTED_PROXIES` to choose which proxies nginx takes client IPs from
### Changed
- Termix now uses calendar versions (year, month, patch), starting with 26.10.0
- Redesigned host defaults with admin, user and folder levels
- Simpler host status: online or offline, kept live without a refresh
- Bug reports and feature requests now go to each repo instead of the Support repo
### Fixed
- A lot of bugs across the app, the desktop app and every plugin
- Several security issues reported through GitHub advisories
### Removed
- Downgrading to 2.8 is no longer supported. A 2.8 install has to start 2.9 once before upgrading
- The donation reminder popup
-75
View File
@@ -1,75 +0,0 @@
# Contributing
Termix is a small core plus plugins. This repo is the core: hosts, credentials, users, sharing, sessions, the app shell and the plugin runtime. Every other feature lives in its own `Plugin-<Name>` repo. If your change is about the terminal, file manager, Docker or any other plugin feature, open it in that plugin's repo instead.
## Prerequisites
- [Node.js](https://nodejs.org/en/download/) 22.12 or newer (built with v24)
- [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- [Git](https://git-scm.com/downloads)
## Installation
1. Clone the repository:
```sh
git clone https://github.com/Termix-SSH/Termix
```
2. Install the dependencies:
```sh
npm install
```
## Running the development server
```sh
npm run dev
```
This builds everything, starts the backend and then the Vite server at `http://localhost:5173/`. Backend changes restart the backend, and plugin changes in `../Plugins` rebuild and reload that plugin. `npm run dev:electron` does the same with the desktop app, and `npm run dev -- --help` lists the other options.
## Useful commands
```sh
npm run test # run the test suite
npm run type-check # type-check the core
npm run lint # lint and the other repo checks
npm run format # format the code with Prettier
```
## Making a change
1. Fork the [repo](https://github.com/Termix-SSH/Termix).
2. Create a branch:
```sh
git checkout -b feature/my-new-feature
```
3. Make your changes and add or update tests for them.
4. Commit your changes:
```sh
git commit -m "feat: add my new feature"
```
5. Push to your fork:
```sh
git push origin feature/my-new-feature
```
6. Open a pull request against the current `dev-*` branch with a clear description.
## Guidelines
- Follow the existing code style. The frontend uses React, Tailwind CSS and shadcn components in `src/ui/`.
- Never hardcode text. Use `t()` and add the English string to `src/ui/locales/en.json`. That's the only locale file you need to update. Other languages are translated automatically, and if an automatic translation is wrong you can fix it in that language's file.
- Keep the UI flat and square. Avoid rounded corners on panels, cards and dialogs.
- Use the theme variables (like `var(--bg-base)` and `var(--text-primary)`) instead of fixed colors.
- There is one component tree for desktop and mobile. Use `src/ui/hooks/use-mobile.ts` for layout changes.
- Core never names a plugin. If core needs something from a plugin, add a slot, action or component id that the plugin fills.
- Tests live in `src/backend/tests/` and `src/ui/tests/`, mirroring the source tree. `npm run test` must pass.
- Every API route gets an OpenAPI JSDoc comment like the routes around it.
- Use short commit messages that start with `feat:`, `fix:` or `chore:`, and link related issues.
## Building a plugin
Start from the [plugin template](https://github.com/Termix-SSH/Termix-Plugin-Template). It talks to Termix only through `@termix-ssh/plugin-sdk` and has its own build, test and release setup.
## Support
Bugs and ideas for Termix itself go in [this repo](https://github.com/Termix-SSH/Termix/issues/new/choose). Plugin problems go in that plugin's own `Plugin-<Name>` repo. Add as much detail as you can, in English if possible. You can also ask in the [Discord](https://discord.gg/jVQGdvHDrf) server.
-26
View File
@@ -1,26 +0,0 @@
cask "termix" do
version "26.10.1"
sha256 "08a8fdc6ce3f29bd9031b050a4f6364f3ca446f17cb272392f1b3b488cea5d30"
url "https://github.com/Termix-SSH/Termix/releases/download/v#{version}/termix_macos_universal_dmg.dmg"
name "Termix"
desc "Self-hosted, plugin-based server management"
homepage "https://github.com/Termix-SSH/Termix"
livecheck do
url :url
strategy :github_latest
end
auto_updates true
app "Termix.app"
zap trash: [
"~/Library/Application Support/termix",
"~/Library/Caches/com.karmaa.termix",
"~/Library/Caches/com.karmaa.termix.ShipIt",
"~/Library/Preferences/com.karmaa.termix.plist",
"~/Library/Saved Application State/com.karmaa.termix.savedState",
]
end
+17 -199
View File
@@ -1,203 +1,21 @@
Copyright 2026 Luke Gustafson
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2025 Luke Gustafson
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+49 -240
View File
@@ -1,253 +1,62 @@
<div align="center">
# Repo Stats
![GitHub Repo stars](https://img.shields.io/github/stars/LukeGus/Termix?style=flat&label=Stars)
![GitHub forks](https://img.shields.io/github/forks/LukeGus/Termix?style=flat&label=Forks)
![GitHub Release](https://img.shields.io/github/v/release/LukeGus/Termix?style=flat&label=Release)
<a href="https://discord.gg/jVQGdvHDrf"><img alt="Discord" src="https://img.shields.io/discord/1347374268253470720"></a>
#### Top Technologies
[![React Badge](https://img.shields.io/badge/-React-61DBFB?style=flat-square&labelColor=black&logo=react&logoColor=61DBFB)](#)
[![Javascript Badge](https://img.shields.io/badge/-Javascript-F0DB4F?style=flat-square&labelColor=black&logo=javascript&logoColor=F0DB4F)](#)
[![Nodejs Badge](https://img.shields.io/badge/-Nodejs-3C873A?style=flat-square&labelColor=black&logo=node.js&logoColor=3C873A)](#)
[![HTML Badge](https://img.shields.io/badge/-HTML-E34F26?style=flat-square&labelColor=black&logo=html5&logoColor=E34F26)](#)
[![Tailwind CSS Badge](https://img.shields.io/badge/-TailwindCSS-38B2AC?style=flat-square&labelColor=black&logo=tailwindcss&logoColor=38B2AC)](#)
[![Docker Badge](https://img.shields.io/badge/-Docker-2496ED?style=flat-square&labelColor=black&logo=docker&logoColor=2496ED)](#)
[![MongoDB Badge](https://img.shields.io/badge/-MongoDB-47A248?style=flat-square&labelColor=black&logo=mongodb&logoColor=47A248)](#)
[![MUI Joy Badge](https://img.shields.io/badge/-MUI%20Joy-007FFF?style=flat-square&labelColor=black&logo=mui&logoColor=007FFF)](#)
<img src="./public/icon.svg" width="120" height="120" alt="Termix Logo" />
<h1>Termix</h1>
<p>Self-hosted, plugin-based server management</p>
<p>
<img src="https://img.shields.io/github/stars/Termix-SSH/Termix?style=flat&label=Stars&color=F39044&labelColor=1a1a1a" />
<img src="https://img.shields.io/github/forks/Termix-SSH/Termix?style=flat&label=Forks&color=F39044&labelColor=1a1a1a" />
<img src="https://img.shields.io/github/v/release/Termix-SSH/Termix?style=flat&label=Release&color=F39044&labelColor=1a1a1a&v=1" />
<a href="https://discord.gg/jVQGdvHDrf"><img alt="Discord" src="https://img.shields.io/discord/1347374268253470720?color=F39044&labelColor=1a1a1a" /></a>
<a href="https://donate.termix.site/"><img alt="Donate" src="https://img.shields.io/badge/Donate-Support%20Termix-F39044?style=flat&labelColor=1a1a1a" /></a>
<br />
<p align="center">
<a href="https://github.com/LukeGus/Termix">
<img alt="Termix Banner" src=./repo-images/TermixLogo.png style="width: 125px; height: auto;"> </a>
</p>
<p>
<a href="https://donate.termix.site/"><img alt="Donations this month" src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&label=Donations%20this%20month&query=%24.fiatTotal&prefix=%24&url=https%3A%2F%2Ftermix.site%2Fdonation-snapshot.json&color=F39044&labelColor=1a1a1a" /></a>
</p>
If you would like, you can support the project here!\
[![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white)](https://paypal.me/LukeGustafson803)
</div>
# Overview
Termix is an open-source forever free self-hosted SSH (other protocols planned, see [Planned Features](#planned-features)) server management panel inspired by [Nexterm](https://github.com/gnmyt/Nexterm). Its purpose is to provide an all-in-one docker-hosted web solution to manage your servers in one easy place. I'm using this project to help me learn [React](https://github.com/facebook/react), [Vite](https://github.com/vitejs/vite-plugin-react), and [Docker](https://www.docker.com) but also because I could never settle on a server management software that I enjoyed to use.
<br />
> [!WARNING]
> This app is in the VERY early stages of development. Expect bugs, data loss, and unexplainable issues! For that reason, I recommend you securely tunnel your connection to Termix through a VPN.
## Overview
# Features
- SSH
- Split Screen (Up to 4) & Tab System
- User Authentication
- Save Hosts (and easily view, connect, and manage them)
Termix is a free, open source, self-hosted server management platform built on plugins. The core keeps your hosts, credentials, users, sharing and sessions. Everything else, like the SSH terminal, remote desktop, file manager, Docker and metrics, is a plugin you can turn on, off or remove.
# Planned Features
- VNC
- RDP
- SFTP (build in file transfer)
- ChatGPT/Ollama Integration (for commands)
- Apps (like notes, AI, etc)
- Terminal Themes
- User Management (roles, permissions, etc.)
- SSH Tunneling
- More Authentication Methods
- More Security Features (like 2FA, etc.)
It runs on web, desktop and mobile. Think of it as a free, self-hosted Termius.
# Installation
Visit the Termix [Wiki](https://github.com/LukeGus/Termix/wiki) for information on how to install Termix. You can also use these links to go directly to guide. [Docker](https://github.com/LukeGus/Termix/wiki/Docker) or [Manual](https://github.com/LukeGus/Termix/wiki/Manual).
<br />
# Support
If you need help with Termix, you can join the [Discord](https://discord.gg/jVQGdvHDrf) server and visit the support channel. You can also open an issue or open a pull request on the [GitHub](https://github.com/LukeGus/Termix/issues) repo. If you would like to support me financially, you can on [Paypal](https://paypal.me/LukeGustafson803).
## Try the Demo
# Show-off
Try it at [demo.termix.site](https://demo.termix.site/). Any username and password works.
![Demo Image](repo-images/DemoImage1.png)
![Demo Image](repo-images/DemoImage2.png)
<br />
## Core
What every install has, with or without plugins:
- Hosts: Save hosts in nested folders with tags and parent hosts, bulk edit, import and export, and use Quick Connect for one-off connections
- Credentials: Reuse saved passwords and keys across hosts, with host defaults you can set for the server, a user or a folder
- Sharing and Roles: Share hosts, credentials and folders with users or roles at four levels: connect, view, edit and manage
- Accounts: Local accounts, sessions you can see and revoke, trusted devices, API keys and an audit log
- Security: Passwords, keys and other secrets are encrypted per user, and the database can be encrypted on disk
- Desktop Sync: The desktop app runs on its own and can link to a Termix server to keep an offline copy of your account
- Interface: Tabs and split screen, a command palette (double tap left shift), rebindable shortcuts, themes, branding and around 30 languages
- Databases: SQLite by default, with PostgreSQL and MySQL supported too
<br />
## Plugins
Plugins add everything else. The official ones ship with Termix, and admins can install, update or remove plugins from the Plugins tab. Each plugin has its own repo.
| Plugin | What it does |
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [ACME Certificates](https://github.com/Termix-SSH/Plugin-ACME-SSL) | Gets the Termix HTTPS certificate from Let's Encrypt or any ACME provider and renews it for you. |
| [AI Assistant](https://github.com/Termix-SSH/Plugin-AI) | An AI assistant that reads your setup and proposes changes for you to approve. |
| [Alerts](https://github.com/Termix-SSH/Plugin-Alerts) | One inbox for every alert in Termix, with delivery to webhooks, ntfy, Discord and email. |
| [Automations](https://github.com/Termix-SSH/Plugin-Automations) | Run steps on your hosts on a schedule or when something happens. |
| [Docker](https://github.com/Termix-SSH/Plugin-Docker) | Manage Docker and Podman containers on your hosts over SSH, with logs, stats and a console. |
| [File Manager](https://github.com/Termix-SSH/Plugin-File-Manager) | Browse, edit and move files over SFTP, including straight from one server to another. |
| [Fleets](https://github.com/Termix-SSH/Plugin-Fleets) | Group hosts into fleets and run commands, package actions and file transfers on all of them at once. |
| [HashiCorp Vault](https://github.com/Termix-SSH/Plugin-Vault) | Connect to hosts with short-lived SSH certificates signed by HashiCorp Vault. |
| [Homepage](https://github.com/Termix-SSH/Plugin-Homepage) | A drag and drop widget page for your servers and services, plus service links for the dashboard. |
| [Host Metrics](https://github.com/Termix-SSH/Plugin-Host-Metrics) | Live CPU, memory, disk, network and GPU stats, plus tools to manage your hosts. |
| [LDAP](https://github.com/Termix-SSH/Plugin-LDAP) | Sign in with your LDAP or Active Directory username and password. |
| [Network Topology](https://github.com/Termix-SSH/Plugin-Network-Topology) | Draws your hosts and the links between them as a live, interactive graph. |
| [OPKSSH](https://github.com/Termix-SSH/Plugin-OPKSSH) | Connect to hosts with short-lived OpenPubkey SSH certificates after signing in with your identity provider. |
| [Passkeys](https://github.com/Termix-SSH/Plugin-WebAuthn) | Sign in with a passkey or security key instead of a password. |
| [Proxmox](https://github.com/Termix-SSH/Plugin-Proxmox) | Import Proxmox VE guests as hosts, keep them in sync and watch node stats. |
| [Remote Desktop](https://github.com/Termix-SSH/Plugin-Remote-Desktop) | RDP, VNC and Telnet sessions in your browser, with clipboard, file transfer, sharing and recording. |
| [Secret Sources](https://github.com/Termix-SSH/Plugin-Secret-Sources) | Pull host passwords and keys from 1Password when you connect instead of storing them in Termix. |
| [Serial](https://github.com/Termix-SSH/Plugin-Serial) | Open a serial console to a router, switch or other device plugged into your computer. |
| [Session Recording](https://github.com/Termix-SSH/Plugin-Session-Recording) | Record terminal and remote desktop sessions and play them back or download them later. |
| [Session Sharing](https://github.com/Termix-SSH/Plugin-Session-Sharing) | Share live sessions by link or with another user, and present sessions to a group in meeting rooms. |
| [Single sign-on](https://github.com/Termix-SSH/Plugin-SSO) | Sign in with any OpenID Connect provider, GitHub or Google. |
| [Snippets](https://github.com/Termix-SSH/Plugin-Snippets) | Save the commands you run often and run them in a terminal or on many hosts in one click. |
| [SSH Terminal](https://github.com/Termix-SSH/Plugin-SSH-Terminal) | SSH terminal sessions with tabs, split screen, history, macros and a local terminal in the desktop app. |
| [Step CA](https://github.com/Termix-SSH/Plugin-Step-CA) | Connect to hosts with short-lived SSH certificates from a smallstep step-ca server. |
| [Tailscale](https://github.com/Termix-SSH/Plugin-Tailscale) | Add tailnet devices as hosts and connect with Tailscale SSH. |
| [Termix Identity](https://github.com/Termix-SSH/Plugin-Termix-Identity) | Publish your SSH public keys under a public handle and run your own SSH certificate authority. |
| [Tmux Monitor](https://github.com/Termix-SSH/Plugin-Tmux-Monitor) | Browse and control tmux sessions, windows and panes across your hosts. |
| [TOTP](https://github.com/Termix-SSH/Plugin-TOTP) | Two-factor sign in with a code from an authenticator app, with backup codes. |
| [Tunnels](https://github.com/Termix-SSH/Plugin-Tunnels) | SSH port forwarding that reconnects on its own, plus client tunnels from the desktop app. |
| [Usage Statistics](https://github.com/Termix-SSH/Plugin-Telemetry) | Sends a small anonymous daily report so we can see how Termix is used. |
| [Wake-on-LAN](https://github.com/Termix-SSH/Plugin-Wake-On-LAN) | Wake sleeping machines by sending a Wake-on-LAN packet to their MAC address. |
| [Warpgate](https://github.com/Termix-SSH/Plugin-Warpgate) | Connect to hosts through a Warpgate SSH bastion. |
| [Web Endpoint](https://github.com/Termix-SSH/Plugin-Web-Endpoint) | Open a host's web UI inside Termix, directly or over an SSH tunnel. |
| [Workspaces](https://github.com/Termix-SSH/Plugin-Workspaces) | Save your open tabs and split layouts and reopen them in one click. |
Want to build your own? Start from the [plugin template](https://github.com/Termix-SSH/Termix-Plugin-Template). Plugins you can install are listed in the [registry](https://github.com/Termix-SSH/Termix-Registry). Turn on the community registry in Plugins, Registries to install plugins made by other people, or add your own registry. See [submitting a plugin](https://docs.termix.site/develop/community-registry) and [hosting your own registry](https://docs.termix.site/develop/registries#host-your-own-registry).
<br />
## Platform Support
<table align="center">
<tr>
<th align="center">Platform</th>
<th align="center">Distribution</th>
</tr>
<tr>
<td align="center"><b>Web</b></td>
<td>Any modern browser (Chrome, Safari, Firefox) | PWA support</td>
</tr>
<tr>
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
<td>Portable | EXE and MSI Installer | Chocolatey</td>
</tr>
<tr>
<td align="center"><b>Linux</b> <sub>x64/arm64/armv7l</sub></td>
<td>Portable | AUR | AppImage | Deb | Flatpak</td>
</tr>
<tr>
<td align="center"><b>macOS</b> <sub>Universal/x64/arm64, v12.0+</sub></td>
<td>Apple App Store | DMG | Homebrew</td>
</tr>
<tr>
<td align="center"><b>iOS/iPadOS</b> <sub>v15.1+</sub></td>
<td>Apple App Store | IPA</td>
</tr>
<tr>
<td align="center"><b>Android</b> <sub>v7.0+</sub></td>
<td>Google Play Store | APK</td>
</tr>
</table>
<br />
## Installation
See the [docs](https://docs.termix.site/install) for install steps on every platform.
Sample Docker Compose file (you can leave out `guacd` and the network if you don't plan to use the Remote Desktop plugin):
```yaml
services:
termix:
image: ghcr.io/termix-ssh/termix:latest
container_name: termix
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- termix-data:/app/data
environment:
PORT: "8080"
GUACD_HOST: "guacd"
GUACD_TUNNEL_HOST: "termix"
GUACD_RECORDING_PATH: "/termix-data/session_recordings/guacamole"
GUACD_DRIVE_PATH: "/termix-data/rdp-drive"
depends_on:
- guacd
networks:
- termix-net
guacd:
image: guacamole/guacd:1.6.0
container_name: guacd
restart: unless-stopped
volumes:
- termix-data:/termix-data
networks:
- termix-net
volumes:
termix-data:
driver: local
networks:
termix-net:
driver: bridge
```
### Cloud Hosting
If Termix runs on the network it manages, an outage takes it down right when you need it. A VPS keeps it reachable, gives you a static IP and works from anywhere without a VPN or port forward.
[GINERNET](https://docs.termix.site/install/ginernet) sponsors Termix and the docs have a setup guide for their VPS.
<br />
## Donate
Termix has no paid plans. Donations cover servers, domains and development time.
[Donate](https://donate.termix.site/)
<br />
## Sponsors
For a paid sponsor spot, email [mail@termix.site](mailto:mail@termix.site).
<!-- SPONSORS:START -->
<div align="center">
<br />
<a href="https://www.blacksmith.sh/">
<img src="https://termix.site/img/sponsors/blacksmith.svg" height="40" alt="Blacksmith" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://www.cloudflare.com/">
<img src="https://termix.site/img/sponsors/cloudflare.png" height="40" alt="Cloudflare" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://akamai.com/">
<img src="https://termix.site/img/sponsors/akamai.svg" height="40" alt="Akamai" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://aws.amazon.com/">
<img src="https://termix.site/img/sponsors/aws.png" height="40" alt="AWS" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://rackgenius.com/">
<img src="https://termix.site/img/sponsors/rackgenius.png" height="40" alt="Rack Genius" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://ginernet.com/">
<img src="https://termix.site/img/sponsors/ginernet.png" height="40" alt="Ginernet" />
</a>
&nbsp;&nbsp;&nbsp;
<a href="https://www.hetzner.com/?mtm_campaign=termix&mtm_medium=referral&mtm_content=sponsoring_link">
<img src="https://termix.site/img/sponsors/hetzner.png" height="40" alt="Hetzner" />
</a>
</div>
<!-- SPONSORS:END -->
<br />
## Support
Bugs and ideas for Termix itself (login, hosts, credentials, sharing, sync, the app and desktop app) go in this repo: [report a bug](https://github.com/Termix-SSH/Termix/issues/new?template=bug_report.yml) or [request a feature](https://github.com/Termix-SSH/Termix/issues/new?template=feature_request.yml).
Every plugin has its own repo, so a problem with the terminal, file manager, Docker, remote desktop or any other plugin goes in that plugin's repo (see the table above, or use Report an issue on the plugin's page in the app, which fills in your versions for you). Not sure where it goes? Open it here and it will be moved.
You need a GitHub account. Add as much detail as you can, in English if possible.
For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server. Update videos are on [YouTube](https://www.youtube.com/@TermixSSH/videos).
<br />
## License
Apache License 2.0. See [LICENSE](LICENSE).
# License
Distributed under the MIT license. See LICENSE for more information.
-25
View File
@@ -1,25 +0,0 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-lyra",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/ui/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
+44 -127
View File
@@ -1,144 +1,61 @@
# Stage 1: Install dependencies
# The builders only produce JS, so they run on the build machine's own arch
# once instead of under QEMU per target. Rolldown hangs under arm64 emulation.
FROM --platform=$BUILDPLATFORM node:24-slim AS deps
# Stage 1: Build frontend
FROM --platform=$BUILDPLATFORM node:18 AS frontend-builder
WORKDIR /app
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
COPY package*.json ./
COPY packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json
COPY .npmrc ./
COPY vendor ./vendor
COPY scripts/patch-ssh2-agent.cjs ./scripts/
RUN npm ci --ignore-scripts && \
node scripts/patch-ssh2-agent.cjs && \
npm cache clean --force
# Stage 2: Build frontend
FROM deps AS frontend-builder
WORKDIR /app
RUN npm install
COPY . .
RUN npm run build
RUN find public/fonts -name "*.ttf" ! -name "*Regular.ttf" ! -name "*Bold.ttf" ! -name "*Italic.ttf" -delete
RUN npm cache clean --force && \
NODE_OPTIONS="--max-old-space-size=3072" npm run build
# Stage 3: Build backend
FROM deps AS backend-builder
# Stage 2: Build backend
FROM --platform=$BUILDPLATFORM node:18 AS backend-builder
WORKDIR /app
COPY . .
# Bundled plugin downloads show in the plugin store, so keep them between builds.
RUN --mount=type=cache,target=/app/node_modules/.cache/termix-bundled-plugins \
npm run build:backend
# Stage 4: Download OPKSSH binary for the target platform so the image works offline
FROM node:24-slim AS opkssh-downloader
ARG TARGETARCH
ARG OPKSSH_VERSION=v0.16.0
ARG OPKSSH_SHA256_AMD64=c018c3e7baf98612b923e742dd87be38650bf61e3b755fb2bc90de177568b1bf
ARG OPKSSH_SHA256_ARM64=9dd10c2b6ce99cde18e52c054877ca014134b291fd82afe71741c68db4f83d44
WORKDIR /opkssh
RUN apt-get update && apt-get install -y curl ca-certificates && rm -rf /var/lib/apt/lists/*
RUN case "$TARGETARCH" in \
amd64) OPKSSH_ARCH=amd64; OPKSSH_SHA256="$OPKSSH_SHA256_AMD64" ;; \
arm64) OPKSSH_ARCH=arm64; OPKSSH_SHA256="$OPKSSH_SHA256_ARM64" ;; \
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
esac && \
curl -fSL -o "opkssh-linux-${OPKSSH_ARCH}" \
"https://github.com/openpubkey/opkssh/releases/download/${OPKSSH_VERSION}/opkssh-linux-${OPKSSH_ARCH}" && \
echo "$OPKSSH_SHA256 opkssh-linux-${OPKSSH_ARCH}" | sha256sum -c - && \
chmod 755 "opkssh-linux-${OPKSSH_ARCH}" && \
echo -n "$OPKSSH_VERSION" > version.txt
# Stage 5: Production dependencies only
FROM node:24-slim AS production-deps
WORKDIR /app
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
COPY package*.json ./
# The plugin SDK is a workspace package core imports at runtime, so npm ci
# has to see it to link it and install its dependencies.
COPY packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json
COPY .npmrc ./
COPY vendor ./vendor
RUN npm install
COPY src/backend/ ./src/backend/
COPY scripts/patch-ssh2-agent.cjs ./scripts/
# Stage 3: Final production image
FROM mongo:5
# Install Node.js
RUN apt-get update && apt-get install -y \
curl \
nginx \
python3 \
build-essential \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# ssh2's native crypto crashes Node 24 when a cipher is freed, so ssh2 uses
# OpenSSL instead (chacha20-poly1305 is then left out).
RUN npm ci --omit=dev --ignore-scripts && \
node scripts/patch-ssh2-agent.cjs && \
rm -rf node_modules/better-sqlite3/prebuilds && \
npm run build-release --prefix node_modules/better-sqlite3 && \
test -f node_modules/better-sqlite3/build/Release/better_sqlite3.node && \
npm rebuild bcryptjs && \
rm -rf node_modules/ssh2/lib/protocol/crypto/build && \
npm cache clean --force
# Configure nginx
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY --from=frontend-builder /app/dist /usr/share/nginx/html
# Stage 6: Final optimized image
FROM node:24-slim
# Setup backend
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY --from=backend-builder /app/src/backend ./src/backend
# The release the opkssh-downloader stage baked in, so the plugin trusts that
# binary when the build picks a version other than its pinned one.
ARG OPKSSH_VERSION=v0.16.0
ARG OPKSSH_SHA256_AMD64=c018c3e7baf98612b923e742dd87be38650bf61e3b755fb2bc90de177568b1bf
ARG OPKSSH_SHA256_ARM64=9dd10c2b6ce99cde18e52c054877ca014134b291fd82afe71741c68db4f83d44
# Create directories for MongoDB and nginx
RUN mkdir -p /data/db && \
mkdir -p /var/log/nginx && \
mkdir -p /var/lib/nginx && \
mkdir -p /var/log/mongodb && \
chown -R mongodb:mongodb /data/db /var/log/mongodb && \
chown -R www-data:www-data /var/log/nginx /var/lib/nginx
ENV DATA_DIR=/app/data \
OPKSSH_BUNDLED_DIR=/app/opkssh-bundled \
OPKSSH_VERSION=$OPKSSH_VERSION \
OPKSSH_SHA256_AMD64=$OPKSSH_SHA256_AMD64 \
OPKSSH_SHA256_ARM64=$OPKSSH_SHA256_ARM64 \
PORT=8080 \
NODE_ENV=production
# Set environment variables
ENV MONGO_URL=mongodb://localhost:27017/termix \
MONGODB_DATA_DIR=/data/db \
MONGODB_LOG_DIR=/var/log/mongodb
RUN apt-get update && apt-get install -y nginx gettext-base openssl ca-certificates gosu wget && \
update-ca-certificates && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /app/data /app/uploads /app/nginx /tmp/nginx && \
chown -R node:node /app /tmp/nginx && \
chmod 755 /app/data /app/uploads /app/nginx /tmp/nginx
# Create volume for MongoDB data
VOLUME ["/data/db"]
COPY docker/nginx.conf /app/nginx/nginx.conf.template
COPY docker/nginx-https.conf /app/nginx/nginx-https.conf.template
COPY --chown=node:node --from=frontend-builder /app/dist /app/html
COPY --chown=node:node --from=production-deps /app/node_modules /app/node_modules
COPY --chown=node:node --from=backend-builder /app/dist/backend ./dist/backend
# Plugins pinned in docker/bundled-plugins.json, resolved as dist/plugins by
# getBundledPluginsDir().
COPY --chown=node:node --from=backend-builder /app/dist/plugins ./dist/plugins
# Target of node_modules/@termix-ssh/plugin-sdk, which core and the plugins import.
COPY --chown=node:node --from=backend-builder /app/packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json
COPY --chown=node:node --from=backend-builder /app/packages/plugin-sdk/dist ./packages/plugin-sdk/dist
# Pre-baked OPKSSH binary. The opkssh plugin uses it when its checksum
# matches, so an offline install never downloads one.
COPY --chown=node:node --from=opkssh-downloader /opkssh /app/opkssh-bundled
COPY --chown=node:node package.json ./
# Schema for Postgres and MySQL. Unused by the default SQLite deployment, which
# builds its tables at startup instead.
COPY --chown=node:node drizzle ./drizzle
VOLUME ["/app/data"]
EXPOSE ${PORT}
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD wget -q -O /dev/null http://localhost:30001/health || exit 1
# Expose ports
EXPOSE 8080 8081 8082 27017
# Use a entrypoint script to run all services
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
CMD ["/entrypoint.sh"]
-265
View File
@@ -1,265 +0,0 @@
{
"plugins": [
{
"id": "acme-ssl",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-ACME-SSL/releases/download/v1.0.1/acme-ssl-1.0.1.tmxplug",
"sha256": "cb5b7c56a281272cfff229d01a2729a4fa3936de04c0ac487d65bcf8dbd2f38e"
},
{
"id": "ai",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-AI/releases/download/v1.0.1/ai-1.0.1.tmxplug",
"sha256": "2558c91e3e6823b3e18873d7b074762c4d76a1a0b96ff38d2dad13e0404b74a7"
},
{
"id": "alerts",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Alerts/releases/download/v1.0.1/alerts-1.0.1.tmxplug",
"sha256": "f5f233c0ffdeee956e1839a17a323c18ce8eb389a08569c3c6e5e24b698dd72a",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "automations",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Automations/releases/download/v1.0.1/automations-1.0.1.tmxplug",
"sha256": "d2bb5298f8afac3f479ded81d5b1a799d9844b3f4990c8aaf2dd8fed7222428d"
},
{
"id": "docker",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Docker/releases/download/v1.0.1/docker-1.0.1.tmxplug",
"sha256": "00e170e61ea9b7952449ea9ce30b09b983433f8d18d29f523cf3c6f98e7286e3",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "file-manager",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-File-Manager/releases/download/v1.0.2/file-manager-1.0.2.tmxplug",
"sha256": "4c735f5b795a8c676590a0dc25db1bcc6036ac250b9817a61867cebdc17931c6",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "fleets",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Fleets/releases/download/v1.0.1/fleets-1.0.1.tmxplug",
"sha256": "82ac71e69022c4991570e6fed109a9eec79421f081af0a1dcb6d8b8e4251ee3b"
},
{
"id": "homepage",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Homepage/releases/download/v1.0.1/homepage-1.0.1.tmxplug",
"sha256": "573375726f334e37bbf504d9411c4ad2d4bfc4363219ff10dcea6cb2cb00ed32"
},
{
"id": "host-metrics",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Host-Metrics/releases/download/v1.0.2/host-metrics-1.0.2.tmxplug",
"sha256": "433d5297e2fc263f2262b7ea33d7a795c9a970089aea328077861c3f032a23f5",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "ldap",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-LDAP/releases/download/v1.0.1/ldap-1.0.1.tmxplug",
"sha256": "f5ba13c682d11060f373f85a9c7aaa131e587ec59c0498c0dabb013c2e474f5c"
},
{
"id": "network-topology",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Network-Topology/releases/download/v1.0.1/network-topology-1.0.1.tmxplug",
"sha256": "4c8ec1ae0c39b8b72a89e0dce4b20a36df4adc826abe215b8ee9b69b44fe75ca"
},
{
"id": "opkssh",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-OPKSSH/releases/download/v1.0.1/opkssh-1.0.1.tmxplug",
"sha256": "c19eadfe763e4dbecbd9a5e9e753576c577c75296ddc8e0af4cce98c2376b26f"
},
{
"id": "proxmox",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Proxmox/releases/download/v1.0.1/proxmox-1.0.1.tmxplug",
"sha256": "4e0989856e061b9ab1ec25cc727051e03ea5e3dc1603e08895bd54e7ce132db7"
},
{
"id": "remote-desktop",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Remote-Desktop/releases/download/v1.0.2/remote-desktop-1.0.2.tmxplug",
"sha256": "70823a3f5161f6722fee22a9d5fe274dd47623960bc2188429c39a1c7e3e2b39",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "secret-sources",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Secret-Sources/releases/download/v1.0.1/secret-sources-1.0.1.tmxplug",
"sha256": "9f5246325d3128c6376962f4cdbed33e78c487a4837f69235dfdcafeb293229a"
},
{
"id": "serial",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Serial/releases/download/v1.0.1/serial-1.0.1.tmxplug",
"sha256": "9908467fc184e40f108ff750bf2fb8044e000a436f84d0a7121109f4fcd5d121",
"onboarding": {
"recommended": "desktop",
"consent": false
}
},
{
"id": "session-recording",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Session-Recording/releases/download/v1.0.1/session-recording-1.0.1.tmxplug",
"sha256": "45a08f950244c862a3774f9a653b3e3645285edb507a49b5c8da123832faf1e7"
},
{
"id": "session-sharing",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Session-Sharing/releases/download/v1.0.1/session-sharing-1.0.1.tmxplug",
"sha256": "753aa9c0998f194ccf5fc49fd39c133a87f21f21e803014c4bfac6ccc7f9acb1"
},
{
"id": "snippets",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Snippets/releases/download/v1.0.1/snippets-1.0.1.tmxplug",
"sha256": "bf643828beba86677220d802c0df74b441d7954538637f37413f78d59c08585f",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "ssh-terminal",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-SSH-Terminal/releases/download/v1.0.2/ssh-terminal-1.0.2.tmxplug",
"sha256": "e65102a8cd6afdaa890095fa698f3f455a1035729b9fdca800a31cc6e5ed760f",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "sso",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-SSO/releases/download/v1.0.2/sso-1.0.2.tmxplug",
"sha256": "29ab0e4748ee71cb11f9b8dacd630430d284b6ce24561cffae9b4d7fe64e9c3b",
"onboarding": {
"recommended": false,
"consent": false,
"enabledByEnv": ["OIDC_CLIENT_ID"]
}
},
{
"id": "step-ca",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Step-CA/releases/download/v1.0.1/step-ca-1.0.1.tmxplug",
"sha256": "ff4df5bae8c48ff61fe659b6ed69a40ac070862bc423972711a194d558d77cc8"
},
{
"id": "tailscale",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Tailscale/releases/download/v1.0.1/tailscale-1.0.1.tmxplug",
"sha256": "0efe1cbf0f431763611926b6a9e92630ca8c1a89ea57bb796c5d7bd3ce0ab55f"
},
{
"id": "telemetry",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Telemetry/releases/download/v1.0.1/telemetry-1.0.1.tmxplug",
"sha256": "c7f70e149c67a570f2b004c2cceeba27ffe8cbd95cd4df4bbffd880cc3451469",
"onboarding": {
"recommended": true,
"consent": true
}
},
{
"id": "termix-identity",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Termix-Identity/releases/download/v1.0.1/termix-identity-1.0.1.tmxplug",
"sha256": "dc423ec53f012fdcc2cdccd872ac01353025e4da2ee9fea0d2a2d5988518a5a0"
},
{
"id": "tmux-monitor",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Tmux-Monitor/releases/download/v1.0.2/tmux-monitor-1.0.2.tmxplug",
"sha256": "ea41f1f90bdeab6164cfa2a594f359492afa123ed06990afa338be69a9d974c6"
},
{
"id": "totp",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-TOTP/releases/download/v1.0.1/totp-1.0.1.tmxplug",
"sha256": "4a083871663c2f6034904bd49bb02c2d4dd4ed4d50fbce1522172dae84991474",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "tunnels",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Tunnels/releases/download/v1.0.2/tunnels-1.0.2.tmxplug",
"sha256": "44799c0bdbcc576fb0e1c1ecce96f283d2cc32b44ac5aeeb7253f8ca0b650860",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "vault",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Vault/releases/download/v1.0.1/vault-1.0.1.tmxplug",
"sha256": "c828eeebe37bf576020908e16a28e73073fc77178062b323058624f557a8c72a"
},
{
"id": "wake-on-lan",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Wake-On-LAN/releases/download/v1.0.1/wake-on-lan-1.0.1.tmxplug",
"sha256": "5301d26138cb37812e8ef1fc5d3df688415517d545667b365fca2f41166a8402"
},
{
"id": "warpgate",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Warpgate/releases/download/v1.0.1/warpgate-1.0.1.tmxplug",
"sha256": "2f3b3169101ff04d0ea6229b31270ba0b5b8ff2c3d16e3f21a9e6c569d463ea8"
},
{
"id": "web-endpoint",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Web-Endpoint/releases/download/v1.0.1/web-endpoint-1.0.1.tmxplug",
"sha256": "88477231ab3c93206c99393c6cc8628c1ff8159af6978d0d09db969c28e1d637"
},
{
"id": "webauthn",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-WebAuthn/releases/download/v1.0.1/webauthn-1.0.1.tmxplug",
"sha256": "f9ccd5d6fe110b5c7ad8b90fd95e0a201e03d440462c2c3f6559406222364464",
"onboarding": {
"recommended": true,
"consent": false
}
},
{
"id": "workspaces",
"source": "tmxplug",
"url": "https://github.com/Termix-SSH/Plugin-Workspaces/releases/download/v1.0.1/workspaces-1.0.1.tmxplug",
"sha256": "17f53044318d6f53ef1e332d204f6469c7043fd9985c4d1eae16b6a1c3248153",
"onboarding": {
"recommended": true,
"consent": false
}
}
]
}
-42
View File
@@ -1,42 +0,0 @@
services:
termix-dev:
build:
context: ..
dockerfile: docker/Dockerfile
container_name: termix-dev
restart: unless-stopped
ports:
- "8081:8080"
volumes:
- termix-dev-data:/app/data
environment:
PORT: "8080"
NODE_ENV: development
GUACD_HOST: "guacd-dev"
GUACD_TUNNEL_HOST: "termix-dev"
GUACD_RECORDING_PATH: "/termix-data/session_recordings/guacamole"
GUACD_DRIVE_PATH: "/termix-data/rdp-drive"
# REDIS_URL: "redis://redis:6379"
# TERMIX_REDIS_PREFIX: "termix:collab"
# TERMIX_STEP_CA_REDIS_PREFIX: "termix:step-ca"
depends_on:
- guacd-dev
networks:
- termix-dev-net
guacd-dev:
image: guacamole/guacd:1.6.0
container_name: guacd-dev
restart: unless-stopped
volumes:
- termix-dev-data:/termix-data
networks:
- termix-dev-net
volumes:
termix-dev-data:
driver: local
networks:
termix-dev-net:
driver: bridge
-59
View File
@@ -1,59 +0,0 @@
services:
termix:
image: ghcr.io/termix-ssh/termix:latest
container_name: termix
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- termix-data:/app/data
environment:
PORT: "8080"
GUACD_HOST: "guacd"
GUACD_TUNNEL_HOST: "termix"
GUACD_RECORDING_PATH: "/termix-data/session_recordings/guacamole"
# Where guacd stores files for RDP drive redirection (one folder per
# user is created underneath). Must be writable by guacd's user.
GUACD_DRIVE_PATH: "/termix-data/rdp-drive"
# Multi-instance deployments should point every backend at the same
# Redis. Without it, collaboration rooms keep their single-instance
# in-memory fallback.
# REDIS_URL: "redis://redis:6379"
# TERMIX_REDIS_PREFIX: "termix:collab"
# TERMIX_STEP_CA_REDIS_PREFIX: "termix:step-ca"
# Hardened deployments can require keys from environment variables or
# Docker secrets mounted through JWT_SECRET_FILE, DATABASE_KEY_FILE,
# ENCRYPTION_KEY_FILE and INTERNAL_AUTH_TOKEN_FILE.
# TERMIX_REQUIRE_EXTERNAL_SECRETS: "true"
# Trusted reverse-proxy authentication is disabled by default. When
# enabled, do not expose this container directly to untrusted clients.
# TRUSTED_PROXY_AUTH_ENABLED: "true"
# List the address your authenticating proxy connects from, as this
# container sees it (for a proxy container on termix-net, its IP there).
# Keep it narrow: published ports can reach the container from the
# Docker gateway, so do not trust whole private ranges.
# TRUSTED_PROXY_AUTH_TRUSTED_PROXIES: "172.20.0.10"
# TRUSTED_PROXY_AUTH_ROLE_MAP: '{"operators":["user"]}'
# TRUSTED_PROXY_AUTH_USERNAME_HEADER: "x-forwarded-username"
# TRUSTED_PROXY_AUTH_ROLE_HEADER: "x-forwarded-role"
depends_on:
- guacd
networks:
- termix-net
guacd:
image: guacamole/guacd:1.6.0
container_name: guacd
restart: unless-stopped
volumes:
- termix-data:/termix-data
networks:
- termix-net
volumes:
termix-data:
driver: local
networks:
termix-net:
driver: bridge
+23 -208
View File
@@ -1,217 +1,32 @@
#!/bin/sh
#!/bin/bash
set -e
PUID=${PUID:-1000}
PGID=${PGID:-1000}
# Start MongoDB
echo "Starting MongoDB..."
mongod --fork --dbpath $MONGODB_DATA_DIR --logpath $MONGODB_LOG_DIR/mongodb.log
if [ "$(id -u)" = "0" ]; then
if [ "$PUID" = "0" ]; then
echo "Running as root (PUID=0, PGID=$PGID)"
chown -R root:root /app/data /app/uploads /tmp/nginx 2>/dev/null || true
else
echo "Setting up user permissions (PUID: $PUID, PGID: $PGID)..."
groupmod -o -g "$PGID" node 2>/dev/null || true
usermod -o -u "$PUID" node 2>/dev/null || true
chown -R node:node /app/data /app/uploads /app/html /tmp/nginx 2>/dev/null || true
echo "User node is now UID: $PUID, GID: $PGID"
exec gosu node:node "$0" "$@"
fi
fi
DATA_DIR=${DATA_DIR:-/app/data}
RUNTIME_ENABLE_SSL_SET=${ENABLE_SSL+x}
RUNTIME_ENABLE_SSL=${ENABLE_SSL-}
RUNTIME_SSL_PORT_SET=${SSL_PORT+x}
RUNTIME_SSL_PORT=${SSL_PORT-}
RUNTIME_SSL_CERT_PATH_SET=${SSL_CERT_PATH+x}
RUNTIME_SSL_CERT_PATH=${SSL_CERT_PATH-}
RUNTIME_SSL_KEY_PATH_SET=${SSL_KEY_PATH+x}
RUNTIME_SSL_KEY_PATH=${SSL_KEY_PATH-}
RUNTIME_SSL_DOMAIN_SET=${SSL_DOMAIN+x}
RUNTIME_SSL_DOMAIN=${SSL_DOMAIN-}
if [ -f "$DATA_DIR/.env" ]; then
echo "Loading persisted SSL settings from $DATA_DIR/.env"
set -a
. "$DATA_DIR/.env"
set +a
fi
[ "$RUNTIME_ENABLE_SSL_SET" = "x" ] && ENABLE_SSL=$RUNTIME_ENABLE_SSL
[ "$RUNTIME_SSL_PORT_SET" = "x" ] && SSL_PORT=$RUNTIME_SSL_PORT
[ "$RUNTIME_SSL_CERT_PATH_SET" = "x" ] && SSL_CERT_PATH=$RUNTIME_SSL_CERT_PATH
[ "$RUNTIME_SSL_KEY_PATH_SET" = "x" ] && SSL_KEY_PATH=$RUNTIME_SSL_KEY_PATH
[ "$RUNTIME_SSL_DOMAIN_SET" = "x" ] && SSL_DOMAIN=$RUNTIME_SSL_DOMAIN
export PORT=${PORT:-8080}
export ENABLE_SSL=${ENABLE_SSL:-false}
export SSL_PORT=${SSL_PORT:-8443}
export SSL_CERT_PATH=${SSL_CERT_PATH:-/app/data/ssl/termix.crt}
export SSL_KEY_PATH=${SSL_KEY_PATH:-/app/data/ssl/termix.key}
export TERMIX_SSL_TERMINATED_BY_NGINX=true
echo "Configuring web UI to run on port: $PORT"
if [ "$ENABLE_SSL" = "true" ]; then
echo "SSL enabled - using HTTPS configuration with redirect"
NGINX_CONF_SOURCE="/app/nginx/nginx-https.conf.template"
else
echo "SSL disabled - using HTTP-only configuration (default)"
NGINX_CONF_SOURCE="/app/nginx/nginx.conf.template"
fi
mkdir -p /tmp/nginx
envsubst '${PORT} ${SSL_PORT} ${SSL_CERT_PATH} ${SSL_KEY_PATH}' < $NGINX_CONF_SOURCE > /tmp/nginx/nginx.conf
# Which proxies may set the client IP through X-Forwarded-For. Unset keeps
# the private ranges in the template.
if [ -n "$TRUSTED_PROXIES" ]; then
REAL_IP_LINES=""
for proxy in $(echo "$TRUSTED_PROXIES" | tr ',' ' '); do
case "$proxy" in
*[!0-9a-fA-F:./]*) echo "Ignoring invalid TRUSTED_PROXIES entry: $proxy" ;;
*) REAL_IP_LINES="${REAL_IP_LINES} set_real_ip_from ${proxy};\n" ;;
esac
done
echo "Trusting X-Forwarded-For from: $TRUSTED_PROXIES"
awk -v lines="$REAL_IP_LINES" '
/# BEGIN TRUSTED_PROXIES/ { printf "%s", lines; skip = 1; next }
/# END TRUSTED_PROXIES/ { skip = 0; next }
!skip
' /tmp/nginx/nginx.conf > /tmp/nginx/nginx.conf.tmp
mv /tmp/nginx/nginx.conf.tmp /tmp/nginx/nginx.conf
fi
if [ "$ENABLE_SSL" = "true" ] && [ "$PORT" = "$SSL_PORT" ]; then
echo "HTTP and HTTPS use port $SSL_PORT; disabling the HTTP redirect listener"
sed -i '/# BEGIN HTTP_REDIRECT_SERVER/,/# END HTTP_REDIRECT_SERVER/d' /tmp/nginx/nginx.conf
fi
mkdir -p /app/data /app/uploads
chmod 755 /app/data /app/uploads 2>/dev/null || true
if [ -w /app/data ]; then
echo "Data directory is writable"
else
echo "WARNING: Data directory is not writable. Plugins that keep files (OPKSSH, recordings) may fail."
ls -ld /app/data
fi
if [ "$ENABLE_SSL" = "true" ]; then
echo "Checking SSL certificate configuration..."
mkdir -p /app/data/ssl
chmod 755 /app/data/ssl 2>/dev/null || true
DOMAIN=${SSL_DOMAIN:-localhost}
if [ -f "/app/data/ssl/termix.crt" ] && [ -f "/app/data/ssl/termix.key" ]; then
echo "SSL certificates found, checking validity..."
if openssl x509 -in /app/data/ssl/termix.crt -checkend 2592000 -noout >/dev/null 2>&1; then
echo "SSL certificates are valid and will be reused for domain: $DOMAIN"
else
SUBJECT=$(openssl x509 -in /app/data/ssl/termix.crt -noout -subject 2>/dev/null | sed 's/^subject=//')
ISSUER=$(openssl x509 -in /app/data/ssl/termix.crt -noout -issuer 2>/dev/null | sed 's/^issuer=//')
# Only replace our own self-signed certificate. A CA-issued one is
# kept until it expires, even when nothing renews it.
if [ -n "$SUBJECT" ] && [ "$SUBJECT" = "$ISSUER" ]; then
echo "Self-signed SSL certificate is expired or expiring soon, regenerating..."
rm -f /app/data/ssl/termix.crt /app/data/ssl/termix.key
else
echo "WARNING: SSL certificate expires within 30 days and was not issued by Termix. Renew it."
fi
fi
else
echo "SSL certificates not found, will generate new ones..."
fi
if [ ! -f "/app/data/ssl/termix.crt" ] || [ ! -f "/app/data/ssl/termix.key" ]; then
echo "Generating SSL certificates for domain: $DOMAIN"
cat > /app/data/ssl/openssl.conf << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C=US
ST=State
L=City
O=Termix
OU=IT Department
CN=$DOMAIN
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $DOMAIN
DNS.2 = localhost
DNS.3 = 127.0.0.1
IP.1 = 127.0.0.1
IP.2 = ::1
IP.3 = 0.0.0.0
EOF
openssl genrsa -out /app/data/ssl/termix.key 2048
openssl req -new -x509 -key /app/data/ssl/termix.key -out /app/data/ssl/termix.crt -days 365 -config /app/data/ssl/openssl.conf -extensions v3_req
chmod 600 /app/data/ssl/termix.key
chmod 644 /app/data/ssl/termix.crt
rm -f /app/data/ssl/openssl.conf
echo "SSL certificates generated successfully for domain: $DOMAIN"
fi
fi
# Wait for MongoDB to be ready
echo "Waiting for MongoDB to start..."
until mongosh --eval "print(\"waited for connection\")" > /dev/null 2>&1; do
sleep 0.5
done
echo "MongoDB has started"
# Start nginx
echo "Starting nginx..."
nginx -c /tmp/nginx/nginx.conf
nginx
# Inject runtime BASE_PATH into frontend if configured
if [ -n "$BASE_PATH" ]; then
echo "Injecting BASE_PATH: $BASE_PATH"
# Strip trailing slash for use as a path prefix
CLEAN_BASE_PATH="${BASE_PATH%/}"
case "$CLEAN_BASE_PATH" in
/*) ;;
*) echo "BASE_PATH must start with /" >&2; exit 1 ;;
esac
case "$CLEAN_BASE_PATH" in
*[!A-Za-z0-9_./~-]*) echo "BASE_PATH contains unsupported characters" >&2; exit 1 ;;
esac
find /app/html -name "index.html" -exec sed -i "s|name=\"termix-base-path\" content=\"\"|name=\"termix-base-path\" content=\"$CLEAN_BASE_PATH\"|g" {} \;
# Patch sw.js static asset paths with the base path prefix
find /app/html -name "sw.js" -exec sed -i "s|__TERMIX_SW_BASE_PATH__|$CLEAN_BASE_PATH|g" {} \;
else
# No base path - replace placeholder with empty string so paths stay absolute from root
find /app/html -name "sw.js" -exec sed -i "s|__TERMIX_SW_BASE_PATH__||g" {} \;
fi
echo "Starting backend services..."
# Change to app directory
cd /app
export NODE_ENV=production
if [ -f "package.json" ]; then
VERSION=$(grep '"version"' package.json | sed 's/.*"version": *"\([^"]*\)".*/\1/')
if [ -n "$VERSION" ]; then
export VERSION
else
echo "Warning: Could not extract version from package.json"
fi
else
echo "Warning: package.json not found"
fi
# Start the SSH service
echo "Starting SSH service..."
node src/backend/ssh.cjs &
exec node dist/backend/backend/starter.js
# Start the database service
echo "Starting database service..."
node src/backend/database.cjs &
# Keep the container running and show MongoDB logs
echo "All services started. Tailing MongoDB logs..."
tail -f $MONGODB_LOG_DIR/mongodb.log
-487
View File
@@ -1,487 +0,0 @@
worker_processes 1;
master_process off;
pid /tmp/nginx/nginx.pid;
error_log /tmp/nginx/error.log warn;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
access_log /tmp/nginx/access.log;
client_body_temp_path /tmp/nginx/client_body;
proxy_temp_path /tmp/nginx/proxy_temp;
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
scgi_temp_path /tmp/nginx/scgi_temp;
sendfile on;
keepalive_timeout 65;
# Static assets only. API responses arrive already gzipped from the node
# backend, and gzip_proxied would otherwise have nginx decompress and
# recompress them for nothing.
gzip on;
gzip_vary on;
gzip_min_length 2048;
gzip_comp_level 5;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/wasm
image/svg+xml;
client_header_timeout 300s;
# BEGIN TRUSTED_PROXIES
set_real_ip_from 127.0.0.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
# END TRUSTED_PROXIES
real_ip_header X-Forwarded-For;
real_ip_recursive on;
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
map $http_x_forwarded_host $proxy_x_forwarded_host {
default $http_x_forwarded_host;
'' $http_host;
}
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' '';
}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# BEGIN HTTP_REDIRECT_SERVER
server {
listen ${PORT};
server_name _;
# ACME http-01 checks arrive on plain HTTP and must not be redirected.
location ^~ /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
return 301 https://$host:${SSL_PORT}$request_uri;
}
}
# END HTTP_REDIRECT_SERVER
server {
listen ${SSL_PORT} ssl;
server_name _;
client_max_body_size 50m;
absolute_redirect off;
ssl_certificate ${SSL_CERT_PATH};
ssl_certificate_key ${SSL_KEY_PATH};
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
location ^~ /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location = /sw.js {
root /app/html;
expires off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri =404;
}
location = /manifest.json {
root /app/html;
expires off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri =404;
}
location ^~ /assets/ {
root /app/html;
expires 1y;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location ^~ /fonts/ {
root /app/html;
expires 1y;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location ^~ /icons/ {
root /app/html;
expires 30d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=2592000" always;
try_files $uri =404;
}
location ~* ^/[^/]+\.(js|css|png|jpe?g|gif|ico|svg|webp|woff2?|ttf|eot)$ {
root /app/html;
expires 30d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=2592000" always;
try_files $uri =404;
}
location ~* \.map$ {
access_log off;
log_not_found off;
return 404;
}
location / {
root /app/html;
index index.html index.htm;
expires off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'sha256-0FPou4vGHwupDie6Xais6VntpJ4yaK9E6TyhXLwZe8o='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri $uri/ /index.html;
}
location ~ ^/users/sessions(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/users(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
# The TCP peer before set_real_ip_from rewrites $remote_addr from
# X-Forwarded-For; trusted proxy login checks this address.
proxy_set_header X-Termix-Proxy-Peer $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
}
location ~ ^/version(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/releases(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/rbac(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/credentials(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location ~ ^/ssh-auth(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/sync(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/audit-logs(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/plugins(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Plugin frontend bundles, stylesheets and locales. Public like
# /assets; the backend picks the cache policy from the ?v= query.
location ^~ /plugin-assets/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Every plugin's HTTP routes. One block, no per-plugin entries: a
# plugin serves at /plugin-api/<id>/ on the main backend and never
# gets a port or a block of its own. Buffering is off so SSE streams
# (AI, Proxmox discovery) reach the browser as they are produced, and
# request buffering is off so large uploads stream through.
location ^~ /plugin-api/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_request_buffering off;
client_max_body_size 0;
}
# Every plugin's WebSockets: terminal, Guacamole display, Docker
# console. Served at /plugin-ws/<id>/<path> from the same backend.
location ^~ /plugin-ws/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_request_buffering off;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
}
location ~ ^/open-tabs(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/user-preferences(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/host-sidebar(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/credential-sidebar(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/ui-preferences(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/database(/.*)?$ {
client_max_body_size 5G;
client_body_timeout 300s;
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
proxy_buffering off;
}
location ~ ^/encryption(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location /host/quick-connect {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
# A terminal is quiet most of the time - waiting on a prompt, reading
# output, running something long. Every console plugin dials this route
# (terminal, serial, remote desktop), and nginx's default
# proxy_read_timeout of 60s closed the socket about a minute into any
# pause, which reaches the user as the SSH server disconnecting. The
# plugin WebSocket routes below already ask for 86400s; this is the same
# class of socket, so it asks for the same.
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_buffering off;
}
location /host/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# File manager routes (recent/pinned/shortcuts, SFTP, transfers) moved
# to the file-manager plugin, served under /plugin-api/file-manager/.
# Session logs moved to the session-recording plugin, served under
# /plugin-api/session-recording/.
location /health {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Service links and the homepage widget canvas moved to the homepage
# plugin, served under /plugin-api/homepage/.
location /dashboard/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
error_page 500 502 503 504 /50x.html;
# Only nginx's own errors land here (the backend is down or too slow).
# There is no 50x page, so answer directly instead of a 404.
location = /50x.html {
internal;
default_type application/json;
return 502 '{"error":"The Termix server is not responding"}';
}
}
}
+29 -429
View File
@@ -1,456 +1,56 @@
worker_processes 1;
master_process off;
pid /tmp/nginx/nginx.pid;
error_log /tmp/nginx/error.log warn;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
include mime.types;
default_type application/octet-stream;
server_tokens off;
access_log /tmp/nginx/access.log;
client_body_temp_path /tmp/nginx/client_body;
proxy_temp_path /tmp/nginx/proxy_temp;
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
scgi_temp_path /tmp/nginx/scgi_temp;
sendfile on;
keepalive_timeout 65;
# Static assets only. API responses arrive already gzipped from the node
# backend, and gzip_proxied would otherwise have nginx decompress and
# recompress them for nothing.
gzip on;
gzip_vary on;
gzip_min_length 2048;
gzip_comp_level 5;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/wasm
image/svg+xml;
client_header_timeout 300s;
# BEGIN TRUSTED_PROXIES
set_real_ip_from 127.0.0.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
# END TRUSTED_PROXIES
real_ip_header X-Forwarded-For;
real_ip_recursive on;
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
map $http_x_forwarded_host $proxy_x_forwarded_host {
default $http_x_forwarded_host;
'' $http_host;
}
map $http_x_forwarded_port $proxy_x_forwarded_port {
default $http_x_forwarded_port;
'' '';
}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
sendfile on;
keepalive_timeout 65;
server {
listen ${PORT};
server_name _;
client_max_body_size 50m;
absolute_redirect off;
add_header X-Content-Type-Options nosniff always;
location ^~ /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location = /sw.js {
root /app/html;
expires off;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri =404;
}
location = /manifest.json {
root /app/html;
expires off;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri =404;
}
location ^~ /assets/ {
root /app/html;
expires 1y;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location ^~ /fonts/ {
root /app/html;
expires 1y;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location ^~ /icons/ {
root /app/html;
expires 30d;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=2592000" always;
try_files $uri =404;
}
location ~* ^/[^/]+\.(js|css|png|jpe?g|gif|ico|svg|webp|woff2?|ttf|eot)$ {
root /app/html;
expires 30d;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "public, max-age=2592000" always;
try_files $uri =404;
}
location ~* \.map$ {
access_log off;
log_not_found off;
return 404;
}
listen 8080;
server_name localhost;
# Serve the React app
location / {
root /app/html;
index index.html index.htm;
expires off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'sha256-0FPou4vGHwupDie6Xais6VntpJ4yaK9E6TyhXLwZe8o='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; connect-src 'self' http: https: ws: wss:; media-src 'self' data: blob: http: https:; worker-src 'self' blob:; frame-src http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header X-Content-Type-Options nosniff always;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
try_files $uri $uri/ /index.html;
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~ ^/users/sessions(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/users(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
# The TCP peer before set_real_ip_from rewrites $remote_addr from
# X-Forwarded-For; trusted proxy login checks this address.
proxy_set_header X-Termix-Proxy-Peer $realip_remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
}
location ~ ^/version(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/releases(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/rbac(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/credentials(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location ~ ^/ssh-auth(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/sync(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/audit-logs(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/plugins(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Plugin frontend bundles, stylesheets and locales. Public like
# /assets; the backend picks the cache policy from the ?v= query.
location ^~ /plugin-assets/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Every plugin's HTTP routes. One block, no per-plugin entries: a
# plugin serves at /plugin-api/<id>/ on the main backend and never
# gets a port or a block of its own. Buffering is off so SSE streams
# (AI, Proxmox discovery) reach the browser as they are produced, and
# request buffering is off so large uploads stream through.
location ^~ /plugin-api/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_request_buffering off;
client_max_body_size 0;
}
# Every plugin's WebSockets: terminal, Guacamole display, Docker
# console. Served at /plugin-ws/<id>/<path> from the same backend.
location ^~ /plugin-ws/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 10s;
proxy_buffering off;
proxy_request_buffering off;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
}
location ~ ^/open-tabs(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/user-preferences(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/host-sidebar(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/credential-sidebar(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/ui-preferences(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location ~ ^/database(/.*)?$ {
client_max_body_size 5G;
client_body_timeout 300s;
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
proxy_buffering off;
}
location ~ ^/encryption(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
location /host/quick-connect {
proxy_pass http://127.0.0.1:30001;
# Proxy SSH socket requests
location /ssh.io/ {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $http_host;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
# A terminal is quiet most of the time - waiting on a prompt, reading
# output, running something long. Every console plugin dials this route
# (terminal, serial, remote desktop), and nginx's default
# proxy_read_timeout of 60s closed the socket about a minute into any
# pause, which reaches the user as the SSH server disconnecting. The
# plugin WebSocket routes below already ask for 86400s; this is the same
# class of socket, so it asks for the same.
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_buffering off;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /host/ {
proxy_pass http://127.0.0.1:30001;
# Proxy MongoDB socket requests
location /database.io/ {
proxy_pass http://127.0.0.1:8082;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Proto $scheme;
}
# File manager routes (recent/pinned/shortcuts, SFTP, transfers) moved
# to the file-manager plugin, served under /plugin-api/file-manager/.
# Session logs moved to the session-recording plugin, served under
# /plugin-api/session-recording/.
location /health {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
# Service links and the homepage widget canvas moved to the homepage
# plugin, served under /plugin-api/homepage/.
location /dashboard/ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
error_page 500 502 503 504 /50x.html;
# Only nginx's own errors land here (the backend is down or too slow).
# There is no 50x page, so answer directly instead of a 404.
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
internal;
default_type application/json;
return 502 '{"error":"The Termix server is not responding"}';
root /usr/share/nginx/html;
}
}
}
}
-7
View File
@@ -1,7 +0,0 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "mysql",
schema: "./src/backend/database/db/schema.mysql.ts",
out: "./drizzle/mysql",
});
-7
View File
@@ -1,7 +0,0 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./src/backend/database/db/schema.pg.ts",
out: "./drizzle/postgres",
});
-7
View File
@@ -1,7 +0,0 @@
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./src/backend/database/db/schema.ts",
out: "./drizzle/sqlite",
});
-890
View File
@@ -1,890 +0,0 @@
CREATE TABLE `alert_firings` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`rule_id` int NOT NULL,
`host_id` int NOT NULL,
`host_name` text NOT NULL,
`fired_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`resolved_at` text,
`value` double,
`message` text NOT NULL,
`severity` text NOT NULL DEFAULT ('warning'),
`acknowledged` boolean NOT NULL DEFAULT false,
CONSTRAINT `alert_firings_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `alert_rule_channels` (
`id` int AUTO_INCREMENT NOT NULL,
`rule_id` int NOT NULL,
`channel_id` int NOT NULL,
CONSTRAINT `alert_rule_channels_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `alert_rules` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int,
`name` varchar(255) NOT NULL,
`enabled` boolean NOT NULL DEFAULT true,
`trigger_type` text NOT NULL,
`threshold_value` double,
`threshold_duration_seconds` int,
`cooldown_minutes` int NOT NULL DEFAULT 15,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `alert_rules_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `api_keys` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`token_hash` text NOT NULL,
`token_prefix` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text,
`last_used_at` text,
`is_active` boolean NOT NULL DEFAULT true,
CONSTRAINT `api_keys_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `audit_logs` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255),
`username` text NOT NULL,
`action` text NOT NULL,
`resource_type` text NOT NULL,
`resource_id` text,
`resource_name` text,
`details` text,
`ip_address` text,
`user_agent` text,
`success` boolean NOT NULL,
`error_message` text,
`timestamp` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `audit_logs_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `c2s_tunnel_presets` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`config` text NOT NULL,
`platform` text,
`computer_name` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `c2s_tunnel_presets_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `command_history` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`command` text NOT NULL,
`executed_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `command_history_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `dashboard_service_links` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`label` text NOT NULL,
`url` text NOT NULL,
`order` int NOT NULL DEFAULT 0,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `dashboard_service_links_id` PRIMARY KEY(`id`),
CONSTRAINT `dashboard_service_links_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `dismissed_alerts` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`alert_id` text NOT NULL,
`dismissed_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `dismissed_alerts_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `file_manager_pinned` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`path` text NOT NULL,
`pinned_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `file_manager_pinned_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `file_manager_recent` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`path` text NOT NULL,
`last_opened` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `file_manager_recent_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `file_manager_shortcuts` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`name` varchar(255) NOT NULL,
`path` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `file_manager_shortcuts_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `homepage_items` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`type_id` text NOT NULL,
`title` text,
`config` text NOT NULL DEFAULT ('{}'),
`folder_id` int,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `homepage_items_id` PRIMARY KEY(`id`),
CONSTRAINT `homepage_items_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `homepage_layouts` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`layout` text NOT NULL DEFAULT ('{}'),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `homepage_layouts_id` PRIMARY KEY(`id`),
CONSTRAINT `homepage_layouts_user_id_unique` UNIQUE(`user_id`)
);
--> statement-breakpoint
CREATE TABLE `host_access` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`user_id` varchar(255),
`role_id` int,
`granted_by` varchar(255) NOT NULL,
`permission_level` text NOT NULL DEFAULT ('connect'),
`expires_at` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`last_accessed_at` text,
`access_count` int NOT NULL DEFAULT 0,
CONSTRAINT `host_access_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `host_health_checks` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`checks` text NOT NULL,
`interval_seconds` int NOT NULL DEFAULT 300,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `host_health_checks_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_host_health_checks_user_host` UNIQUE(`user_id`,`host_id`)
);
--> statement-breakpoint
CREATE TABLE `host_health_history` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`check_id` text NOT NULL,
`ts` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`ok` boolean NOT NULL,
`latency_ms` int,
`detail` text,
CONSTRAINT `host_health_history_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `host_metrics_history` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`ts` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`cpu_percent` double,
`mem_percent` double,
`disk_percent` double,
`net_rx_bytes` int,
`net_tx_bytes` int,
CONSTRAINT `host_metrics_history_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `host_metrics_preferences` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`layout` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `host_metrics_preferences_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_host_metrics_prefs_user_host` UNIQUE(`user_id`,`host_id`)
);
--> statement-breakpoint
CREATE TABLE `ssh_data` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`connection_type` text NOT NULL DEFAULT ('ssh'),
`name` varchar(255),
`ip` text NOT NULL,
`port` int NOT NULL,
`username` text NOT NULL,
`folder` text,
`tags` text,
`pin` boolean NOT NULL DEFAULT false,
`auth_type` text NOT NULL,
`use_warpgate` boolean NOT NULL DEFAULT false,
`share_ssh_auth` boolean NOT NULL DEFAULT false,
`force_keyboard_interactive` text,
`password` text,
`key` text,
`key_password` text,
`key_type` text,
`sudo_password` text,
`autostart_password` text,
`autostart_key` text,
`autostart_key_password` text,
`credential_id` int,
`override_credential_username` boolean,
`vault_profile_id` int,
`enable_terminal` boolean NOT NULL DEFAULT true,
`enable_session_logging` boolean NOT NULL DEFAULT true,
`allow_session_sharing` boolean NOT NULL DEFAULT true,
`enable_command_history` boolean NOT NULL DEFAULT true,
`enable_tunnel` boolean NOT NULL DEFAULT true,
`tunnel_connections` text,
`jump_hosts` text,
`enable_file_manager` boolean NOT NULL DEFAULT true,
`scp_legacy` boolean NOT NULL DEFAULT false,
`enable_docker` boolean NOT NULL DEFAULT false,
`enable_tmux_monitor` boolean NOT NULL DEFAULT false,
`show_terminal_in_sidebar` boolean NOT NULL DEFAULT true,
`show_file_manager_in_sidebar` boolean NOT NULL DEFAULT false,
`show_tunnel_in_sidebar` boolean NOT NULL DEFAULT false,
`show_docker_in_sidebar` boolean NOT NULL DEFAULT false,
`show_server_stats_in_sidebar` boolean NOT NULL DEFAULT false,
`default_path` text,
`stats_config` text,
`docker_config` text,
`enable_proxmox` boolean NOT NULL DEFAULT false,
`proxmox_config` text,
`terminal_config` text,
`quick_actions` text,
`notes` text,
`enable_ssh` boolean NOT NULL DEFAULT true,
`enable_rdp` boolean NOT NULL DEFAULT false,
`enable_vnc` boolean NOT NULL DEFAULT false,
`enable_telnet` boolean NOT NULL DEFAULT false,
`ssh_port` int DEFAULT 22,
`rdp_port` int DEFAULT 3389,
`vnc_port` int DEFAULT 5900,
`telnet_port` int DEFAULT 23,
`rdp_credential_id` int,
`rdp_user` text,
`rdp_password` text,
`rdp_domain` text,
`rdp_security` text,
`rdp_ignore_cert` boolean DEFAULT false,
`vnc_credential_id` int,
`vnc_password` text,
`vnc_user` text,
`telnet_user` text,
`telnet_password` text,
`telnet_credential_id` int,
`rdp_auth_type` text,
`vnc_auth_type` text,
`telnet_auth_type` text,
`domain` text,
`security` text,
`ignore_cert` boolean DEFAULT false,
`guacamole_config` text,
`use_socks5` boolean,
`socks5_host` text,
`socks5_port` int,
`socks5_username` text,
`socks5_password` text,
`socks5_proxy_chain` text,
`connection_origin` text,
`mac_address` text,
`wol_broadcast_address` text,
`port_knock_sequence` text,
`host_key_fingerprint` text,
`host_key_type` text,
`host_key_algorithm` text DEFAULT ('sha256'),
`host_key_first_seen` text,
`host_key_last_verified` text,
`host_key_changed_count` int DEFAULT 0,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ssh_data_id` PRIMARY KEY(`id`),
CONSTRAINT `ssh_data_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `network_topology` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`topology` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `network_topology_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `notification_channels` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`type` text NOT NULL,
`config` text NOT NULL,
`enabled` boolean NOT NULL DEFAULT true,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `notification_channels_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `opkssh_tokens` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`ssh_cert` text NOT NULL,
`private_key` text NOT NULL,
`email` text,
`sub` text,
`issuer` text,
`audience` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text NOT NULL,
`last_used` text,
CONSTRAINT `opkssh_tokens_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_opkssh_tokens_user_host` UNIQUE(`user_id`,`host_id`)
);
--> statement-breakpoint
CREATE TABLE `recent_activity` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`type` text NOT NULL,
`host_id` int NOT NULL,
`host_name` text,
`timestamp` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `recent_activity_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `roles` (
`id` int AUTO_INCREMENT NOT NULL,
`name` varchar(255) NOT NULL,
`display_name` text NOT NULL,
`description` text,
`is_system` boolean NOT NULL DEFAULT false,
`permissions` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `roles_id` PRIMARY KEY(`id`),
CONSTRAINT `roles_name_unique` UNIQUE(`name`)
);
--> statement-breakpoint
CREATE TABLE `session_recordings` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`user_id` varchar(255),
`username` text,
`access_id` int,
`started_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`ended_at` text,
`duration` int,
`commands` text,
`dangerous_actions` text,
`recording_path` text,
`protocol` varchar(255) NOT NULL DEFAULT 'ssh',
`format` text NOT NULL DEFAULT ('text'),
`terminated_by_owner` boolean DEFAULT false,
`termination_reason` text,
CONSTRAINT `session_recordings_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `session_share_participants` (
`id` int AUTO_INCREMENT NOT NULL,
`share_id` varchar(255) NOT NULL,
`user_id` varchar(255),
`guest_label` text,
`joined_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`left_at` text,
CONSTRAINT `session_share_participants_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `session_shares` (
`id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`owner_user_id` varchar(255) NOT NULL,
`protocol` varchar(255) NOT NULL,
`session_id` text NOT NULL,
`tab_instance_id` text,
`share_type` text NOT NULL,
`target_user_id` varchar(255),
`link_token` varchar(255),
`permission_level` text NOT NULL DEFAULT ('read-only'),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text NOT NULL,
`revoked_at` text,
`last_joined_at` text,
`join_count` int NOT NULL DEFAULT 0,
CONSTRAINT `session_shares_id` PRIMARY KEY(`id`),
CONSTRAINT `session_shares_link_token_unique` UNIQUE(`link_token`)
);
--> statement-breakpoint
CREATE TABLE `sessions` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`jwt_token` text NOT NULL,
`device_type` text NOT NULL,
`device_info` text NOT NULL,
`oidc_sub` text,
`oidc_sid` text,
`sso_provider_id` int,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text NOT NULL,
`last_active_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `sessions_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `settings` (
`key` varchar(255) NOT NULL,
`value` text NOT NULL,
CONSTRAINT `settings_key` PRIMARY KEY(`key`)
);
--> statement-breakpoint
CREATE TABLE `shared_host_auth_overrides` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`protocol` varchar(255) NOT NULL DEFAULT 'ssh',
`credential_id` int NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `shared_host_auth_overrides_id` PRIMARY KEY(`id`),
CONSTRAINT `shared_host_auth_overrides_host_user_protocol_unique` UNIQUE(`host_id`,`user_id`,`protocol`)
);
--> statement-breakpoint
CREATE TABLE `shared_host_secrets` (
`id` int AUTO_INCREMENT NOT NULL,
`host_access_id` int NOT NULL,
`target_user_id` varchar(255) NOT NULL,
`protocol` varchar(255) NOT NULL DEFAULT 'ssh',
`source_type` text NOT NULL DEFAULT ('credential'),
`original_credential_id` int,
`encrypted_username` text,
`encrypted_auth_type` text,
`encrypted_password` text,
`encrypted_key` text,
`encrypted_key_password` text,
`encrypted_key_type` text,
`encrypted_domain` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `shared_host_secrets_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_shared_host_secrets_scope` UNIQUE(`host_access_id`,`target_user_id`,`protocol`)
);
--> statement-breakpoint
CREATE TABLE `snippet_access` (
`id` int AUTO_INCREMENT NOT NULL,
`snippet_id` int NOT NULL,
`user_id` varchar(255),
`role_id` int,
`granted_by` varchar(255) NOT NULL,
`permission_level` text NOT NULL DEFAULT ('view'),
`expires_at` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `snippet_access_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `snippet_folders` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`color` text,
`icon` text,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `snippet_folders_id` PRIMARY KEY(`id`),
CONSTRAINT `snippet_folders_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `snippets` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`content` text NOT NULL,
`description` text,
`folder` text,
`order` int NOT NULL DEFAULT 0,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`host_filter` text,
CONSTRAINT `snippets_id` PRIMARY KEY(`id`),
CONSTRAINT `snippets_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `ssh_credential_usage` (
`id` int AUTO_INCREMENT NOT NULL,
`credential_id` int NOT NULL,
`host_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`used_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ssh_credential_usage_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `ssh_credentials` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`folder` text,
`tags` text,
`auth_type` text NOT NULL,
`username` text,
`password` text,
`key` text,
`private_key` text,
`public_key` text,
`key_password` text,
`key_type` text,
`detected_key_type` text,
`cert_public_key` text,
`usage_count` int NOT NULL DEFAULT 0,
`last_used` text,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ssh_credentials_id` PRIMARY KEY(`id`),
CONSTRAINT `ssh_credentials_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `ssh_folders` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`color` text,
`icon` text,
`credential_id` int,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ssh_folders_id` PRIMARY KEY(`id`),
CONSTRAINT `ssh_folders_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `sso_providers` (
`id` int AUTO_INCREMENT NOT NULL,
`name` varchar(255) NOT NULL,
`type` text NOT NULL,
`enabled` boolean NOT NULL DEFAULT true,
`display_order` int NOT NULL DEFAULT 0,
`config` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `sso_providers_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `sync_tombstones` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`entity_type` text NOT NULL,
`sync_id` varchar(255) NOT NULL,
`deleted_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `sync_tombstones_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `termix_identities` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`handle` varchar(255) NOT NULL,
`description` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `termix_identities_id` PRIMARY KEY(`id`),
CONSTRAINT `termix_identities_user_id_unique` UNIQUE(`user_id`),
CONSTRAINT `termix_identities_handle_unique` UNIQUE(`handle`)
);
--> statement-breakpoint
CREATE TABLE `termix_identity_ca` (
`id` int AUTO_INCREMENT NOT NULL,
`identity_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`public_key` text NOT NULL,
`private_key` text NOT NULL,
`validity_days` int NOT NULL DEFAULT 90,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `termix_identity_ca_id` PRIMARY KEY(`id`),
CONSTRAINT `termix_identity_ca_identity_id_unique` UNIQUE(`identity_id`)
);
--> statement-breakpoint
CREATE TABLE `termix_identity_keys` (
`id` int AUTO_INCREMENT NOT NULL,
`identity_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`public_key` text NOT NULL,
`key_type` text NOT NULL,
`algorithm` text NOT NULL,
`label` text,
`comment` text,
`source` text NOT NULL DEFAULT ('manual'),
`credential_id` int,
`enabled` boolean NOT NULL DEFAULT true,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `termix_identity_keys_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `tmux_session_tags` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`session_name` text NOT NULL,
`tag` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `tmux_session_tags_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `transfer_recent` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`source_host_id` int NOT NULL,
`dest_host_id` int NOT NULL,
`dest_path` text NOT NULL,
`dest_path_label` text NOT NULL,
`last_used` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `transfer_recent_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `trusted_devices` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`device_fingerprint` text NOT NULL,
`device_type` text NOT NULL,
`device_info` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text NOT NULL,
`last_used_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `trusted_devices_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `user_open_tabs` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`tab_type` text NOT NULL,
`host_id` int,
`label` text NOT NULL,
`tab_order` int NOT NULL DEFAULT 0,
`backend_session_id` text,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `user_open_tabs_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `user_preferences` (
`user_id` varchar(255) NOT NULL,
`reopen_tabs_on_login` boolean NOT NULL DEFAULT false,
`theme` text,
`font_size` text,
`accent_color` text,
`language` text,
`storage_mode` text,
`command_autocomplete` boolean,
`command_palette_enabled` boolean,
`show_host_tags` boolean,
`host_tray_on_click` boolean,
`pin_app_rail` boolean,
`expand_app_rail_on_hover` boolean,
`folders_collapsed` boolean,
`confirm_snippet_execution` boolean,
`disable_update_check` boolean,
`confirm_tab_close` boolean,
`hidden_rail_tabs` text,
`compact_host_view` boolean,
`status_color_scheme` text,
`custom_themes` text,
`custom_keybindings` text,
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `user_preferences_user_id` PRIMARY KEY(`user_id`)
);
--> statement-breakpoint
CREATE TABLE `user_roles` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`role_id` int NOT NULL,
`granted_by` varchar(255),
`granted_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `user_roles_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_user_roles_user_role` UNIQUE(`user_id`,`role_id`)
);
--> statement-breakpoint
CREATE TABLE `users` (
`id` varchar(255) NOT NULL,
`username` text NOT NULL,
`password_hash` text NOT NULL,
`is_admin` boolean NOT NULL DEFAULT false,
`is_oidc` boolean NOT NULL DEFAULT false,
`oidc_identifier` text,
`sso_provider_id` int,
`client_id` text,
`client_secret` text,
`issuer_url` text,
`authorization_url` text,
`token_url` text,
`identifier_path` text,
`name_path` text,
`scopes` text DEFAULT ('openid email profile'),
`totp_secret` text,
`totp_enabled` boolean NOT NULL DEFAULT false,
`totp_backup_codes` text,
`registered_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`donation_modal_dismissed` boolean NOT NULL DEFAULT false,
CONSTRAINT `users_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `vault_profiles` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`folder` text,
`tags` text,
`vault_addr` text NOT NULL,
`vault_namespace` text,
`oidc_mount` text,
`oidc_role` text,
`ssh_mount` text,
`ssh_role` text NOT NULL,
`valid_principals` text,
`key_type` text,
`shared` boolean NOT NULL DEFAULT false,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `vault_profiles_id` PRIMARY KEY(`id`),
CONSTRAINT `vault_profiles_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
CREATE TABLE `vault_tokens` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`profile_id` int NOT NULL,
`ssh_cert` text NOT NULL,
`private_key` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`expires_at` text NOT NULL,
`last_used` text,
CONSTRAINT `vault_tokens_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_vault_tokens_user_profile` UNIQUE(`user_id`,`profile_id`)
);
--> statement-breakpoint
CREATE TABLE `webauthn_credentials` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`credential_id` text NOT NULL,
`public_key` text NOT NULL,
`counter` int NOT NULL DEFAULT 0,
`device_type` text,
`backed_up` boolean NOT NULL DEFAULT false,
`transports` text,
`user_verification` text NOT NULL DEFAULT ('preferred'),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`last_used_at` text,
CONSTRAINT `webauthn_credentials_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `alert_firings` ADD CONSTRAINT `alert_firings_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `alert_firings` ADD CONSTRAINT `alert_firings_rule_id_alert_rules_id_fk` FOREIGN KEY (`rule_id`) REFERENCES `alert_rules`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `alert_rule_channels` ADD CONSTRAINT `alert_rule_channels_rule_id_alert_rules_id_fk` FOREIGN KEY (`rule_id`) REFERENCES `alert_rules`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `alert_rule_channels` ADD CONSTRAINT `alert_rule_channels_channel_id_notification_channels_id_fk` FOREIGN KEY (`channel_id`) REFERENCES `notification_channels`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `alert_rules` ADD CONSTRAINT `alert_rules_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `alert_rules` ADD CONSTRAINT `alert_rules_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `api_keys` ADD CONSTRAINT `api_keys_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `audit_logs` ADD CONSTRAINT `audit_logs_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `c2s_tunnel_presets` ADD CONSTRAINT `c2s_tunnel_presets_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `command_history` ADD CONSTRAINT `command_history_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `command_history` ADD CONSTRAINT `command_history_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `dashboard_service_links` ADD CONSTRAINT `dashboard_service_links_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `dismissed_alerts` ADD CONSTRAINT `dismissed_alerts_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_pinned` ADD CONSTRAINT `file_manager_pinned_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_pinned` ADD CONSTRAINT `file_manager_pinned_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_recent` ADD CONSTRAINT `file_manager_recent_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_recent` ADD CONSTRAINT `file_manager_recent_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_shortcuts` ADD CONSTRAINT `file_manager_shortcuts_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `file_manager_shortcuts` ADD CONSTRAINT `file_manager_shortcuts_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `homepage_items` ADD CONSTRAINT `homepage_items_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `homepage_layouts` ADD CONSTRAINT `homepage_layouts_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_access` ADD CONSTRAINT `host_access_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_access` ADD CONSTRAINT `host_access_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_access` ADD CONSTRAINT `host_access_role_id_roles_id_fk` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_access` ADD CONSTRAINT `host_access_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_health_checks` ADD CONSTRAINT `host_health_checks_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_health_checks` ADD CONSTRAINT `host_health_checks_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_health_history` ADD CONSTRAINT `host_health_history_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_health_history` ADD CONSTRAINT `host_health_history_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_metrics_history` ADD CONSTRAINT `host_metrics_history_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_metrics_preferences` ADD CONSTRAINT `host_metrics_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `host_metrics_preferences` ADD CONSTRAINT `host_metrics_preferences_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_vault_profile_id_vault_profiles_id_fk` FOREIGN KEY (`vault_profile_id`) REFERENCES `vault_profiles`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_rdp_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`rdp_credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_vnc_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`vnc_credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_telnet_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`telnet_credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `network_topology` ADD CONSTRAINT `network_topology_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `notification_channels` ADD CONSTRAINT `notification_channels_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `opkssh_tokens` ADD CONSTRAINT `opkssh_tokens_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `opkssh_tokens` ADD CONSTRAINT `opkssh_tokens_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `recent_activity` ADD CONSTRAINT `recent_activity_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `recent_activity` ADD CONSTRAINT `recent_activity_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_recordings` ADD CONSTRAINT `session_recordings_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_recordings` ADD CONSTRAINT `session_recordings_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_recordings` ADD CONSTRAINT `session_recordings_access_id_host_access_id_fk` FOREIGN KEY (`access_id`) REFERENCES `host_access`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_share_participants` ADD CONSTRAINT `session_share_participants_share_id_session_shares_id_fk` FOREIGN KEY (`share_id`) REFERENCES `session_shares`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_share_participants` ADD CONSTRAINT `session_share_participants_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_shares` ADD CONSTRAINT `session_shares_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_shares` ADD CONSTRAINT `session_shares_owner_user_id_users_id_fk` FOREIGN KEY (`owner_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `session_shares` ADD CONSTRAINT `session_shares_target_user_id_users_id_fk` FOREIGN KEY (`target_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `sessions` ADD CONSTRAINT `sessions_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` ADD CONSTRAINT `shared_host_auth_overrides_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` ADD CONSTRAINT `shared_host_auth_overrides_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` ADD CONSTRAINT `shared_host_auth_overrides_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_secrets` ADD CONSTRAINT `shared_host_secrets_host_access_id_host_access_id_fk` FOREIGN KEY (`host_access_id`) REFERENCES `host_access`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_secrets` ADD CONSTRAINT `shared_host_secrets_target_user_id_users_id_fk` FOREIGN KEY (`target_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_host_secrets` ADD CONSTRAINT `shared_host_secrets_original_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`original_credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippet_access` ADD CONSTRAINT `snippet_access_snippet_id_snippets_id_fk` FOREIGN KEY (`snippet_id`) REFERENCES `snippets`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippet_access` ADD CONSTRAINT `snippet_access_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippet_access` ADD CONSTRAINT `snippet_access_role_id_roles_id_fk` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippet_access` ADD CONSTRAINT `snippet_access_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippet_folders` ADD CONSTRAINT `snippet_folders_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `snippets` ADD CONSTRAINT `snippets_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_credential_usage` ADD CONSTRAINT `ssh_credential_usage_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_credential_usage` ADD CONSTRAINT `ssh_credential_usage_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_credential_usage` ADD CONSTRAINT `ssh_credential_usage_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_credentials` ADD CONSTRAINT `ssh_credentials_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_folders` ADD CONSTRAINT `ssh_folders_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ssh_folders` ADD CONSTRAINT `ssh_folders_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `sync_tombstones` ADD CONSTRAINT `sync_tombstones_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identities` ADD CONSTRAINT `termix_identities_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identity_ca` ADD CONSTRAINT `termix_identity_ca_identity_id_termix_identities_id_fk` FOREIGN KEY (`identity_id`) REFERENCES `termix_identities`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identity_ca` ADD CONSTRAINT `termix_identity_ca_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identity_keys` ADD CONSTRAINT `termix_identity_keys_identity_id_termix_identities_id_fk` FOREIGN KEY (`identity_id`) REFERENCES `termix_identities`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identity_keys` ADD CONSTRAINT `termix_identity_keys_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `termix_identity_keys` ADD CONSTRAINT `termix_identity_keys_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `tmux_session_tags` ADD CONSTRAINT `tmux_session_tags_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `tmux_session_tags` ADD CONSTRAINT `tmux_session_tags_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `transfer_recent` ADD CONSTRAINT `transfer_recent_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `transfer_recent` ADD CONSTRAINT `transfer_recent_source_host_id_ssh_data_id_fk` FOREIGN KEY (`source_host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `transfer_recent` ADD CONSTRAINT `transfer_recent_dest_host_id_ssh_data_id_fk` FOREIGN KEY (`dest_host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `trusted_devices` ADD CONSTRAINT `trusted_devices_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_open_tabs` ADD CONSTRAINT `user_open_tabs_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_open_tabs` ADD CONSTRAINT `user_open_tabs_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_preferences` ADD CONSTRAINT `user_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_roles` ADD CONSTRAINT `user_roles_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_roles` ADD CONSTRAINT `user_roles_role_id_roles_id_fk` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_roles` ADD CONSTRAINT `user_roles_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `vault_profiles` ADD CONSTRAINT `vault_profiles_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `vault_tokens` ADD CONSTRAINT `vault_tokens_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `vault_tokens` ADD CONSTRAINT `vault_tokens_profile_id_vault_profiles_id_fk` FOREIGN KEY (`profile_id`) REFERENCES `vault_profiles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `webauthn_credentials` ADD CONSTRAINT `webauthn_credentials_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-10
View File
@@ -1,10 +0,0 @@
CREATE TABLE `host_sidebar_preferences` (
`user_id` varchar(255) NOT NULL,
`data` text NOT NULL,
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `host_sidebar_preferences_user_id` PRIMARY KEY(`user_id`)
);
--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `sort_order` int;--> statement-breakpoint
ALTER TABLE `ssh_folders` ADD `sort_order` int;--> statement-breakpoint
ALTER TABLE `host_sidebar_preferences` ADD CONSTRAINT `host_sidebar_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-10
View File
@@ -1,10 +0,0 @@
CREATE TABLE `credential_sidebar_preferences` (
`user_id` varchar(255) NOT NULL,
`data` text NOT NULL,
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `credential_sidebar_preferences_user_id` PRIMARY KEY(`user_id`)
);
--> statement-breakpoint
ALTER TABLE `ssh_credentials` ADD `pin` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_credentials` ADD `sort_order` int;--> statement-breakpoint
ALTER TABLE `credential_sidebar_preferences` ADD CONSTRAINT `credential_sidebar_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `snippets` ADD `is_note` boolean DEFAULT false NOT NULL;
@@ -1,28 +0,0 @@
CREATE TABLE `proxmox_node_history` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`ts` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`cpu_percent` double,
`mem_percent` double,
`disk_percent` double,
`net_rx_bytes` int,
`net_tx_bytes` int,
CONSTRAINT `proxmox_node_history_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `proxmox_stats_preferences` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`host_id` int NOT NULL,
`layout` text NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `proxmox_stats_preferences_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_proxmox_stats_prefs_user_host` UNIQUE(`user_id`,`host_id`)
);
--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `enable_proxmox_stats` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `proxmox_stats_config` text;--> statement-breakpoint
ALTER TABLE `proxmox_node_history` ADD CONSTRAINT `proxmox_node_history_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `proxmox_stats_preferences` ADD CONSTRAINT `proxmox_stats_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `proxmox_stats_preferences` ADD CONSTRAINT `proxmox_stats_preferences_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `ssh_data` ADD `enable_terminal_toolbar` boolean DEFAULT true NOT NULL;
@@ -1,45 +0,0 @@
CREATE TABLE `fleet_inventory` (
`id` int AUTO_INCREMENT NOT NULL,
`host_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`os_pretty_name` text,
`kernel` text,
`architecture` text,
`hostname` text,
`uptime_seconds` int,
`ip` text,
`package_manager` text,
`collected_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `fleet_inventory_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_fleet_inventory_host` UNIQUE(`host_id`,`user_id`)
);
--> statement-breakpoint
CREATE TABLE `fleet_members` (
`id` int AUTO_INCREMENT NOT NULL,
`fleet_id` int NOT NULL,
`host_id` int NOT NULL,
`added_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `fleet_members_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_fleet_members_fleet_host` UNIQUE(`fleet_id`,`host_id`)
);
--> statement-breakpoint
CREATE TABLE `fleets` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`color` text,
`icon` text,
`tag_rules` text,
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `fleets_id` PRIMARY KEY(`id`),
CONSTRAINT `fleets_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
ALTER TABLE `fleet_inventory` ADD CONSTRAINT `fleet_inventory_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `fleet_inventory` ADD CONSTRAINT `fleet_inventory_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `fleet_members` ADD CONSTRAINT `fleet_members_fleet_id_fleets_id_fk` FOREIGN KEY (`fleet_id`) REFERENCES `fleets`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `fleet_members` ADD CONSTRAINT `fleet_members_host_id_ssh_data_id_fk` FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `fleets` ADD CONSTRAINT `fleets_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-2
View File
@@ -1,2 +0,0 @@
ALTER TABLE `ssh_data` ADD `parent_host_id` int;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD CONSTRAINT `ssh_data_parent_host_id_ssh_data_id_fk` FOREIGN KEY (`parent_host_id`) REFERENCES `ssh_data`(`id`) ON DELETE set null ON UPDATE no action;
-18
View File
@@ -1,18 +0,0 @@
CREATE TABLE `user_workspaces` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`color` text,
`icon` text,
`kind` text NOT NULL DEFAULT ('manual'),
`is_default` boolean NOT NULL DEFAULT false,
`payload` text NOT NULL DEFAULT ('{}'),
`sync_id` varchar(255),
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`last_used_at` text,
CONSTRAINT `user_workspaces_id` PRIMARY KEY(`id`),
CONSTRAINT `user_workspaces_sync_id_unique` UNIQUE(`sync_id`)
);
--> statement-breakpoint
ALTER TABLE `user_workspaces` ADD CONSTRAINT `user_workspaces_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-26
View File
@@ -1,26 +0,0 @@
ALTER TABLE `api_keys` MODIFY COLUMN `expires_at` varchar(255);--> statement-breakpoint
ALTER TABLE `audit_logs` MODIFY COLUMN `action` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `audit_logs` MODIFY COLUMN `resource_type` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `audit_logs` MODIFY COLUMN `timestamp` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_access` MODIFY COLUMN `expires_at` varchar(255);--> statement-breakpoint
ALTER TABLE `opkssh_tokens` MODIFY COLUMN `expires_at` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `recent_activity` MODIFY COLUMN `timestamp` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `session_shares` MODIFY COLUMN `expires_at` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `sessions` MODIFY COLUMN `expires_at` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `snippet_access` MODIFY COLUMN `expires_at` varchar(255);--> statement-breakpoint
ALTER TABLE `trusted_devices` MODIFY COLUMN `expires_at` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `vault_tokens` MODIFY COLUMN `expires_at` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `webauthn_credentials` MODIFY COLUMN `credential_id` varchar(255) NOT NULL;--> statement-breakpoint
CREATE INDEX `idx_audit_logs_timestamp` ON `audit_logs` (`timestamp`);--> statement-breakpoint
CREATE INDEX `idx_audit_logs_user_ts` ON `audit_logs` (`user_id`,`timestamp`);--> statement-breakpoint
CREATE INDEX `idx_audit_logs_action_ts` ON `audit_logs` (`action`,`timestamp`);--> statement-breakpoint
CREATE INDEX `idx_audit_logs_resource_ts` ON `audit_logs` (`resource_type`,`timestamp`);--> statement-breakpoint
CREATE INDEX `idx_host_access_user_id` ON `host_access` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_host_access_role_id` ON `host_access` (`role_id`);--> statement-breakpoint
CREATE INDEX `idx_host_access_host_id` ON `host_access` (`host_id`);--> statement-breakpoint
CREATE INDEX `idx_host_access_expires_at` ON `host_access` (`expires_at`);--> statement-breakpoint
CREATE INDEX `idx_ssh_data_user_id` ON `ssh_data` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_data_parent_host` ON `ssh_data` (`parent_host_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_data_credential` ON `ssh_data` (`credential_id`);--> statement-breakpoint
CREATE INDEX `idx_sessions_user_id` ON `sessions` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_sessions_expires_at` ON `sessions` (`expires_at`);
@@ -1,8 +0,0 @@
CREATE TABLE `ui_preferences` (
`user_id` varchar(255) NOT NULL,
`data` text NOT NULL,
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ui_preferences_user_id` PRIMARY KEY(`user_id`)
);
--> statement-breakpoint
ALTER TABLE `ui_preferences` ADD CONSTRAINT `ui_preferences_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;
-32
View File
@@ -1,32 +0,0 @@
ALTER TABLE `alert_firings` MODIFY COLUMN `fired_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `session_recordings` MODIFY COLUMN `started_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `session_shares` MODIFY COLUMN `session_id` varchar(255) NOT NULL;--> statement-breakpoint
CREATE INDEX `idx_alert_firings_rule` ON `alert_firings` (`rule_id`,`fired_at`);--> statement-breakpoint
CREATE INDEX `idx_alert_firings_host` ON `alert_firings` (`host_id`);--> statement-breakpoint
CREATE INDEX `idx_api_keys_user_id` ON `api_keys` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_command_history_user_host` ON `command_history` (`user_id`,`host_id`);--> statement-breakpoint
CREATE INDEX `idx_dismissed_alerts_user_id` ON `dismissed_alerts` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_file_manager_pinned_user` ON `file_manager_pinned` (`user_id`,`host_id`);--> statement-breakpoint
CREATE INDEX `idx_file_manager_recent_user` ON `file_manager_recent` (`user_id`,`host_id`);--> statement-breakpoint
CREATE INDEX `idx_file_manager_shortcuts_user` ON `file_manager_shortcuts` (`user_id`,`host_id`);--> statement-breakpoint
CREATE INDEX `idx_fleet_inventory_user` ON `fleet_inventory` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_fleet_members_host` ON `fleet_members` (`host_id`);--> statement-breakpoint
CREATE INDEX `idx_homepage_items_user_id` ON `homepage_items` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_recent_activity_user_ts` ON `recent_activity` (`user_id`,`timestamp`);--> statement-breakpoint
CREATE INDEX `idx_session_recordings_user_started` ON `session_recordings` (`user_id`,`started_at`);--> statement-breakpoint
CREATE INDEX `idx_session_recordings_host` ON `session_recordings` (`host_id`);--> statement-breakpoint
CREATE INDEX `idx_session_shares_session_id` ON `session_shares` (`session_id`);--> statement-breakpoint
CREATE INDEX `idx_session_shares_host_id` ON `session_shares` (`host_id`);--> statement-breakpoint
CREATE INDEX `idx_snippet_access_user_id` ON `snippet_access` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_snippet_access_snippet_id` ON `snippet_access` (`snippet_id`);--> statement-breakpoint
CREATE INDEX `idx_snippet_access_role_id` ON `snippet_access` (`role_id`);--> statement-breakpoint
CREATE INDEX `idx_snippets_user_id` ON `snippets` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_credential_usage_credential` ON `ssh_credential_usage` (`credential_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_credential_usage_user` ON `ssh_credential_usage` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_credentials_user_id` ON `ssh_credentials` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_ssh_folders_user_id` ON `ssh_folders` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_transfer_recent_user` ON `transfer_recent` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_trusted_devices_user_id` ON `trusted_devices` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_user_open_tabs_user_id` ON `user_open_tabs` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_user_roles_role_id` ON `user_roles` (`role_id`);--> statement-breakpoint
CREATE INDEX `idx_user_workspaces_user_id` ON `user_workspaces` (`user_id`);
-224
View File
@@ -1,224 +0,0 @@
CREATE TABLE `ai_conversations` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`title` text,
`provider_id` int,
`model` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ai_conversations_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `ai_messages` (
`id` int AUTO_INCREMENT NOT NULL,
`conversation_id` int NOT NULL,
`role` text NOT NULL,
`content` text NOT NULL DEFAULT (''),
`tool_calls` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ai_messages_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `ai_proposals` (
`id` int AUTO_INCREMENT NOT NULL,
`conversation_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`kind` text NOT NULL,
`summary` text,
`payload` text NOT NULL DEFAULT ('{}'),
`status` varchar(255) NOT NULL DEFAULT 'pending',
`applied_at` text,
`result_summary` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ai_proposals_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `ai_providers` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`provider_type` text NOT NULL,
`label` varchar(255) NOT NULL,
`base_url` text,
`api_key` text,
`api_key_prefix` text,
`default_model` text,
`enabled` boolean NOT NULL DEFAULT true,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `ai_providers_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_ai_providers_user_label` UNIQUE(`user_id`,`label`)
);
--> statement-breakpoint
CREATE TABLE `automation_channels` (
`id` int AUTO_INCREMENT NOT NULL,
`automation_id` int NOT NULL,
`channel_id` int NOT NULL,
CONSTRAINT `automation_channels_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_automation_channels_pair` UNIQUE(`automation_id`,`channel_id`)
);
--> statement-breakpoint
CREATE TABLE `automation_run_steps` (
`id` int AUTO_INCREMENT NOT NULL,
`run_id` int NOT NULL,
`step_index` int NOT NULL,
`step_id` text NOT NULL,
`step_type` text NOT NULL,
`status` varchar(255) NOT NULL,
`started_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`finished_at` text,
`output` text,
`error` text,
`truncated` boolean NOT NULL DEFAULT false,
CONSTRAINT `automation_run_steps_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `automation_runs` (
`id` int AUTO_INCREMENT NOT NULL,
`automation_id` int NOT NULL,
`user_id` varchar(255) NOT NULL,
`trigger_type` text NOT NULL,
`trigger_context` text,
`status` varchar(255) NOT NULL,
`started_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`finished_at` text,
`duration_ms` int,
`error` text,
`dry_run` boolean NOT NULL DEFAULT false,
`parent_run_id` int,
CONSTRAINT `automation_runs_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `automation_schedules` (
`id` int AUTO_INCREMENT NOT NULL,
`automation_id` int NOT NULL,
`cron` text,
`interval_seconds` int,
`timezone` text,
`next_due_at` varchar(255),
`last_tick_at` text,
CONSTRAINT `automation_schedules_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_automation_schedules_automation` UNIQUE(`automation_id`)
);
--> statement-breakpoint
CREATE TABLE `automation_trigger_state` (
`id` int AUTO_INCREMENT NOT NULL,
`automation_id` int NOT NULL,
`state_key` varchar(255) NOT NULL,
`breach_started_at` text,
`last_fired_at` text,
`last_value` double,
`last_observed_state` text,
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `automation_trigger_state_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_automation_trigger_state_key` UNIQUE(`automation_id`,`state_key`)
);
--> statement-breakpoint
CREATE TABLE `automations` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`enabled` boolean NOT NULL DEFAULT true,
`definition` text NOT NULL,
`definition_version` int NOT NULL DEFAULT 1,
`concurrency_policy` text NOT NULL DEFAULT ('skip'),
`max_run_seconds` int NOT NULL DEFAULT 300,
`dry_run` boolean NOT NULL DEFAULT false,
`last_run_at` text,
`last_run_status` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `automations_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `alert_rules` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `alert_rules` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `api_keys` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `c2s_tunnel_presets` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `c2s_tunnel_presets` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `credential_sidebar_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `dashboard_service_links` MODIFY COLUMN `label` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `dashboard_service_links` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `dashboard_service_links` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `file_manager_shortcuts` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `fleets` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `fleets` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `homepage_items` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `homepage_items` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `homepage_layouts` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_access` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_health_checks` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_health_checks` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_metrics_preferences` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_metrics_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_sidebar_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `network_topology` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `network_topology` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `notification_channels` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `opkssh_tokens` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `proxmox_stats_preferences` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `proxmox_stats_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `roles` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `roles` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `session_shares` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `sessions` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_secrets` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_secrets` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `snippet_access` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `snippet_folders` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `snippet_folders` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `snippets` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `snippets` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_folders` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_folders` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `sso_providers` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `sso_providers` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `termix_identities` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `termix_identities` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `termix_identity_ca` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `termix_identity_ca` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `termix_identity_keys` MODIFY COLUMN `label` varchar(255);--> statement-breakpoint
ALTER TABLE `termix_identity_keys` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `tmux_session_tags` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `trusted_devices` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ui_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `label` varchar(255) NOT NULL;--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_preferences` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_workspaces` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_workspaces` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `vault_profiles` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `vault_profiles` MODIFY COLUMN `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `vault_tokens` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `webauthn_credentials` MODIFY COLUMN `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_preferences` ADD `ai_assistant_enabled` boolean;--> statement-breakpoint
ALTER TABLE `user_preferences` ADD `ai_read_only_commands` boolean;--> statement-breakpoint
ALTER TABLE `ai_conversations` ADD CONSTRAINT `ai_conversations_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ai_messages` ADD CONSTRAINT `ai_messages_conversation_id_ai_conversations_id_fk` FOREIGN KEY (`conversation_id`) REFERENCES `ai_conversations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ai_proposals` ADD CONSTRAINT `ai_proposals_conversation_id_ai_conversations_id_fk` FOREIGN KEY (`conversation_id`) REFERENCES `ai_conversations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ai_proposals` ADD CONSTRAINT `ai_proposals_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `ai_providers` ADD CONSTRAINT `ai_providers_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_channels` ADD CONSTRAINT `automation_channels_automation_id_automations_id_fk` FOREIGN KEY (`automation_id`) REFERENCES `automations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_channels` ADD CONSTRAINT `automation_channels_channel_id_notification_channels_id_fk` FOREIGN KEY (`channel_id`) REFERENCES `notification_channels`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_run_steps` ADD CONSTRAINT `automation_run_steps_run_id_automation_runs_id_fk` FOREIGN KEY (`run_id`) REFERENCES `automation_runs`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_runs` ADD CONSTRAINT `automation_runs_automation_id_automations_id_fk` FOREIGN KEY (`automation_id`) REFERENCES `automations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_runs` ADD CONSTRAINT `automation_runs_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_schedules` ADD CONSTRAINT `automation_schedules_automation_id_automations_id_fk` FOREIGN KEY (`automation_id`) REFERENCES `automations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automation_trigger_state` ADD CONSTRAINT `automation_trigger_state_automation_id_automations_id_fk` FOREIGN KEY (`automation_id`) REFERENCES `automations`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `automations` ADD CONSTRAINT `automations_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_ai_conversations_user` ON `ai_conversations` (`user_id`,`updated_at`);--> statement-breakpoint
CREATE INDEX `idx_ai_messages_conversation` ON `ai_messages` (`conversation_id`,`created_at`);--> statement-breakpoint
CREATE INDEX `idx_ai_proposals_user` ON `ai_proposals` (`user_id`,`status`);--> statement-breakpoint
CREATE INDEX `idx_ai_proposals_conversation` ON `ai_proposals` (`conversation_id`);--> statement-breakpoint
CREATE INDEX `idx_automation_run_steps_run` ON `automation_run_steps` (`run_id`,`step_index`);--> statement-breakpoint
CREATE INDEX `idx_automation_runs_automation` ON `automation_runs` (`automation_id`,`started_at`);--> statement-breakpoint
CREATE INDEX `idx_automation_runs_user` ON `automation_runs` (`user_id`,`started_at`);--> statement-breakpoint
CREATE INDEX `idx_automation_schedules_due` ON `automation_schedules` (`next_due_at`);--> statement-breakpoint
CREATE INDEX `idx_automations_user` ON `automations` (`user_id`,`enabled`);
-2
View File
@@ -1,2 +0,0 @@
ALTER TABLE `user_preferences` ADD `terminal_defaults` text;--> statement-breakpoint
ALTER TABLE `user_preferences` ADD `rdp_defaults` text;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `user_preferences` ADD `terminal_macros` text;
-34
View File
@@ -1,34 +0,0 @@
CREATE TABLE `collab_room_members` (
`id` int AUTO_INCREMENT NOT NULL,
`room_id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`room_role` text NOT NULL DEFAULT ('member'),
`added_by` varchar(255),
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `collab_room_members_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_collab_room_members_room_user` UNIQUE(`room_id`,`user_id`)
);
--> statement-breakpoint
CREATE TABLE `collab_rooms` (
`id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`owner_user_id` varchar(255) NOT NULL,
`persistent` boolean NOT NULL DEFAULT false,
`presenter_user_id` varchar(255),
`stage_protocol` text,
`stage_host_id` int,
`stage_share_id` varchar(255),
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`ended_at` text,
CONSTRAINT `collab_rooms_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `collab_room_members` ADD CONSTRAINT `collab_room_members_room_id_collab_rooms_id_fk` FOREIGN KEY (`room_id`) REFERENCES `collab_rooms`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_room_members` ADD CONSTRAINT `collab_room_members_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_room_members` ADD CONSTRAINT `collab_room_members_added_by_users_id_fk` FOREIGN KEY (`added_by`) REFERENCES `users`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_rooms` ADD CONSTRAINT `collab_rooms_owner_user_id_users_id_fk` FOREIGN KEY (`owner_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_rooms` ADD CONSTRAINT `collab_rooms_presenter_user_id_users_id_fk` FOREIGN KEY (`presenter_user_id`) REFERENCES `users`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_rooms` ADD CONSTRAINT `collab_rooms_stage_host_id_ssh_data_id_fk` FOREIGN KEY (`stage_host_id`) REFERENCES `ssh_data`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `collab_rooms` ADD CONSTRAINT `collab_rooms_stage_share_id_session_shares_id_fk` FOREIGN KEY (`stage_share_id`) REFERENCES `session_shares`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_collab_room_members_user` ON `collab_room_members` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_collab_rooms_owner` ON `collab_rooms` (`owner_user_id`);
-2
View File
@@ -1,2 +0,0 @@
ALTER TABLE `collab_rooms` ADD `guest_link_token` varchar(255);--> statement-breakpoint
ALTER TABLE `collab_rooms` ADD CONSTRAINT `idx_collab_rooms_guest_token` UNIQUE(`guest_link_token`);
-15
View File
@@ -1,15 +0,0 @@
CREATE TABLE `secret_sources` (
`id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`kind` text NOT NULL DEFAULT ('onepassword-connect'),
`base_url` text NOT NULL,
`token` text NOT NULL,
`shared` boolean NOT NULL DEFAULT false,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `secret_sources_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `secret_sources` ADD CONSTRAINT `secret_sources_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_secret_sources_user` ON `secret_sources` (`user_id`);
-42
View File
@@ -1,42 +0,0 @@
CREATE TABLE `credential_access` (
`id` int AUTO_INCREMENT NOT NULL,
`credential_id` int NOT NULL,
`user_id` varchar(255),
`role_id` int,
`granted_by` varchar(255) NOT NULL,
`permission_level` text NOT NULL DEFAULT ('use'),
`expires_at` varchar(255),
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `credential_access_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `shared_credential_secrets` (
`id` int AUTO_INCREMENT NOT NULL,
`credential_access_id` int NOT NULL,
`target_user_id` varchar(255) NOT NULL,
`credential_id` int NOT NULL,
`encrypted_username` text,
`auth_type` text NOT NULL DEFAULT ('password'),
`encrypted_password` text,
`encrypted_key` text,
`encrypted_key_password` text,
`key_type` text,
`public_key` text,
`cert_public_key` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `shared_credential_secrets_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_shared_credential_secrets_scope` UNIQUE(`credential_access_id`,`target_user_id`)
);
--> statement-breakpoint
ALTER TABLE `credential_access` ADD CONSTRAINT `credential_access_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `credential_access` ADD CONSTRAINT `credential_access_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `credential_access` ADD CONSTRAINT `credential_access_role_id_roles_id_fk` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `credential_access` ADD CONSTRAINT `credential_access_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_credential_secrets` ADD CONSTRAINT `shared_cred_secrets_access_id_fk` FOREIGN KEY (`credential_access_id`) REFERENCES `credential_access`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_credential_secrets` ADD CONSTRAINT `shared_credential_secrets_target_user_id_users_id_fk` FOREIGN KEY (`target_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `shared_credential_secrets` ADD CONSTRAINT `shared_credential_secrets_credential_id_ssh_credentials_id_fk` FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_credential_access_user_id` ON `credential_access` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_credential_access_role_id` ON `credential_access` (`role_id`);--> statement-breakpoint
CREATE INDEX `idx_credential_access_credential_id` ON `credential_access` (`credential_id`);--> statement-breakpoint
CREATE INDEX `idx_shared_credential_secrets_target` ON `shared_credential_secrets` (`target_user_id`,`credential_id`);
-22
View File
@@ -1,22 +0,0 @@
CREATE TABLE `folder_access` (
`id` int AUTO_INCREMENT NOT NULL,
`owner_user_id` varchar(255) NOT NULL,
`folder` varchar(255) NOT NULL,
`user_id` varchar(255),
`role_id` int,
`granted_by` varchar(255) NOT NULL,
`permission_level` text NOT NULL DEFAULT ('connect'),
`expires_at` varchar(255),
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `folder_access_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `folder` varchar(255);--> statement-breakpoint
ALTER TABLE `snippets` MODIFY COLUMN `folder` varchar(255);--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `folder` varchar(255);--> statement-breakpoint
ALTER TABLE `vault_profiles` MODIFY COLUMN `folder` varchar(255);--> statement-breakpoint
ALTER TABLE `folder_access` ADD CONSTRAINT `folder_access_owner_user_id_users_id_fk` FOREIGN KEY (`owner_user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `folder_access` ADD CONSTRAINT `folder_access_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `folder_access` ADD CONSTRAINT `folder_access_role_id_roles_id_fk` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `folder_access` ADD CONSTRAINT `folder_access_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_folder_access_owner_folder` ON `folder_access` (`owner_user_id`,`folder`);
-2
View File
@@ -1,2 +0,0 @@
ALTER TABLE `ssh_data` ADD `enable_web_ui` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `web_ui_config` text;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `ssh_data` ADD `enable_ai_assistant` boolean DEFAULT false NOT NULL;
-51
View File
@@ -1,51 +0,0 @@
CREATE TABLE `plugin_install_counts` (
`id` int AUTO_INCREMENT NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`registry_id` varchar(255) NOT NULL,
`count` int NOT NULL DEFAULT 0,
`source` text NOT NULL DEFAULT ('aggregate-telemetry'),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `plugin_install_counts_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_plugin_install_counts_plugin_registry` UNIQUE(`plugin_id`,`registry_id`)
);
--> statement-breakpoint
CREATE TABLE `plugin_permission_grants` (
`id` int AUTO_INCREMENT NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`capability` varchar(255) NOT NULL,
`granted_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`granted_by` varchar(255) NOT NULL,
CONSTRAINT `plugin_permission_grants_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_plugin_permission_grants_plugin_capability` UNIQUE(`plugin_id`,`capability`)
);
--> statement-breakpoint
CREATE TABLE `plugin_registries` (
`id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`url` text NOT NULL,
`kind` text NOT NULL DEFAULT ('community'),
`enabled` boolean NOT NULL DEFAULT true,
`signing_key` text,
`last_checked_at` text,
`last_index_hash` text,
CONSTRAINT `plugin_registries_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `plugins` (
`id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`version` text NOT NULL,
`tier` text NOT NULL DEFAULT ('available'),
`source` text NOT NULL DEFAULT ('community'),
`registry_id` varchar(255),
`state` text NOT NULL DEFAULT ('disabled'),
`installed_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`auto_update` boolean NOT NULL DEFAULT false,
`manifest_json` text NOT NULL,
CONSTRAINT `plugins_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
ALTER TABLE `plugin_permission_grants` ADD CONSTRAINT `plugin_permission_grants_plugin_id_plugins_id_fk` FOREIGN KEY (`plugin_id`) REFERENCES `plugins`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `plugin_permission_grants` ADD CONSTRAINT `plugin_permission_grants_granted_by_users_id_fk` FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_plugins_registry_id` ON `plugins` (`registry_id`);
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `user_preferences` ADD `show_pin_app_rail_button` boolean;
-11
View File
@@ -1,11 +0,0 @@
CREATE TABLE `plugin_storage` (
`id` int AUTO_INCREMENT NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`key` varchar(255) NOT NULL,
`value` text NOT NULL,
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `plugin_storage_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_plugin_storage_plugin_key` UNIQUE(`plugin_id`,`key`)
);
--> statement-breakpoint
ALTER TABLE `plugin_storage` ADD CONSTRAINT `plugin_storage_plugin_id_plugins_id_fk` FOREIGN KEY (`plugin_id`) REFERENCES `plugins`(`id`) ON DELETE cascade ON UPDATE no action;
-3
View File
@@ -1,3 +0,0 @@
DROP TABLE `alert_firings`;--> statement-breakpoint
DROP TABLE `alert_rule_channels`;--> statement-breakpoint
DROP TABLE `alert_rules`;
-3
View File
@@ -1,3 +0,0 @@
ALTER TABLE `plugin_permission_grants` MODIFY COLUMN `granted_by` varchar(255);--> statement-breakpoint
ALTER TABLE `plugin_permission_grants` ADD `source` text DEFAULT ('admin') NOT NULL;--> statement-breakpoint
ALTER TABLE `plugins` ADD `last_error` text;
@@ -1,11 +0,0 @@
CREATE TABLE `plugin_migrations` (
`id` int AUTO_INCREMENT NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`migration_id` varchar(255) NOT NULL,
`checksum` text NOT NULL,
`applied_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `plugin_migrations_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_plugin_migrations_plugin_migration` UNIQUE(`plugin_id`,`migration_id`)
);
--> statement-breakpoint
ALTER TABLE `plugin_migrations` ADD CONSTRAINT `plugin_migrations_plugin_id_plugins_id_fk` FOREIGN KEY (`plugin_id`) REFERENCES `plugins`(`id`) ON DELETE cascade ON UPDATE no action;
-17
View File
@@ -1,17 +0,0 @@
CREATE TABLE `rbac_applied_defaults` (
`id` int AUTO_INCREMENT NOT NULL,
`role_name` varchar(255) NOT NULL,
`permission` varchar(255) NOT NULL,
`applied_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `rbac_applied_defaults_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_rbac_applied_defaults_role_permission` UNIQUE(`role_name`,`permission`)
);
--> statement-breakpoint
CREATE TABLE `rbac_known_permissions` (
`id` int AUTO_INCREMENT NOT NULL,
`permission` varchar(255) NOT NULL,
`plugin_id` varchar(255),
`first_seen_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `rbac_known_permissions_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_rbac_known_permissions_permission` UNIQUE(`permission`)
);
@@ -1,15 +0,0 @@
CREATE TABLE `plugin_settings` (
`id` int AUTO_INCREMENT NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`scope` enum('admin','user','host','secret') NOT NULL,
`scope_id` varchar(255),
`key` varchar(255) NOT NULL,
`value` text,
`encrypted` boolean NOT NULL DEFAULT false,
`updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `plugin_settings_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_plugin_settings_scope_key` UNIQUE(`plugin_id`,`scope`,`scope_id`,`key`)
);
--> statement-breakpoint
ALTER TABLE `plugin_settings` ADD CONSTRAINT `plugin_settings_plugin_id_plugins_id_fk` FOREIGN KEY (`plugin_id`) REFERENCES `plugins`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_plugin_settings_plugin_scope` ON `plugin_settings` (`plugin_id`,`scope`);
-24
View File
@@ -1,24 +0,0 @@
CREATE TABLE `user_external_identities` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`provider_id` varchar(255) NOT NULL,
`subject` varchar(255) NOT NULL,
`email` text,
`created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `user_external_identities_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_user_external_identities_provider_subject` UNIQUE(`provider_id`,`subject`)
);
--> statement-breakpoint
CREATE TABLE `user_second_factors` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`plugin_id` varchar(255) NOT NULL,
`factor_id` varchar(255) NOT NULL,
`enrolled_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `user_second_factors_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_user_second_factors_user_factor` UNIQUE(`user_id`,`plugin_id`,`factor_id`)
);
--> statement-breakpoint
ALTER TABLE `user_external_identities` ADD CONSTRAINT `user_external_identities_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `user_second_factors` ADD CONSTRAINT `user_second_factors_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_user_external_identities_user` ON `user_external_identities` (`user_id`);
@@ -1,5 +0,0 @@
-- user_workspaces moved to the workspaces plugin, whose first migration renames it
-- to p_workspaces_workspaces. Dropping it here would lose every saved workspace,
-- so this only stops drizzle tracking it. SELECT 1 because MySQL rejects an
-- empty query.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- network_topology moved to the network-topology plugin, which adopts (renames)
-- this table in its own migration before this drizzle migration would run.
-- No DROP here: it would delete every row before the plugin's rename applies.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- snippets, snippet_folders and snippet_access moved to the snippets plugin
-- (p_snippets_*), adopted by rename rather than dropped. A real DROP TABLE
-- here would run before the plugin's adoption migration and delete every row.
SELECT 1;
-2
View File
@@ -1,2 +0,0 @@
-- dropped, see plugin adoption (fleets plugin, migrations/mysql/0001_adopt_fleets.sql)
SELECT 1;
-6
View File
@@ -1,6 +0,0 @@
-- proxmox_node_history and proxmox_stats_preferences moved to the proxmox
-- plugin (p_proxmox_*), adopted by rename rather than dropped. The four
-- ssh_data proxmox columns are copied into the plugin's host settings after
-- plugins load, which is after this migration, so a real DROP COLUMN here
-- would delete the data first. The columns stay in place, unused, until 3.0.0.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- file_manager_pinned, file_manager_recent, file_manager_shortcuts and
-- transfer_recent are adopted (renamed, not dropped) by the file-manager
-- plugin's own migration, which runs at plugin activation, after core's
-- drizzle migrations. A DROP TABLE here would run first and delete the data.
SELECT 1;
@@ -1,5 +0,0 @@
-- c2s_tunnel_presets moved to the tunnels plugin, whose first migration renames
-- it to p_tunnels_presets. Dropping it here would lose every saved preset, so
-- this only stops drizzle tracking it. SELECT 1 because MySQL rejects an
-- empty query.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- command_history moved to the ssh-terminal plugin, whose first migration
-- renames it to p_ssh_terminal_command_history. Dropping it here would lose
-- every saved command, so this only stops drizzle tracking it. SELECT 1
-- because MySQL rejects an empty query.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- tmux_session_tags moved to the tmux-monitor plugin, whose first migration
-- renames it to p_tmux_monitor_session_tags. Dropping it here would lose
-- every saved tag, so this only stops drizzle tracking it. SELECT 1 because
-- MySQL rejects an empty query.
SELECT 1;
-6
View File
@@ -1,6 +0,0 @@
-- session_shares, session_share_participants, collab_rooms and
-- collab_room_members moved to the session-sharing plugin, whose first
-- migration renames them. Dropping them here would lose every share and
-- room, so this only stops drizzle tracking them. SELECT 1 because MySQL
-- rejects an empty query.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- session_recordings moved to the session-recording plugin, whose first
-- migration renames it. Dropping it here would lose every recording, so
-- this only stops drizzle tracking it. SELECT 1 because MySQL rejects an
-- empty query.
SELECT 1;
@@ -1,7 +0,0 @@
-- The remote desktop host options (enable_rdp, enable_vnc, enable_telnet,
-- the three ports, rdp_security, rdp_ignore_cert, security, ignore_cert,
-- guacamole_config) and user_preferences.rdp_defaults moved to the
-- remote-desktop plugin's settings. The copy runs after plugins load, which
-- is after this migration, so a real DROP COLUMN here would delete the data
-- first. The columns stay in place, unused, until 3.0.0.
SELECT 1;
@@ -1,9 +0,0 @@
-- host_metrics_preferences, host_health_checks, host_health_history and
-- host_metrics_history moved to the host-metrics plugin, which adopts them by
-- rename in its own first migration. That runs after this one, so a real
-- DROP TABLE here would delete every row first.
SELECT 1;--> statement-breakpoint
-- Status checks are core: the per-host switch and interval, copied out of
-- stats_config at boot by host-status-config-migration.ts.
ALTER TABLE `ssh_data` ADD `status_check_enabled` boolean DEFAULT true NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `status_check_interval` int;
@@ -1,6 +0,0 @@
-- ssh_data.stats_config split: its status half moved to status_check_enabled
-- and status_check_interval, its metrics half to the host-metrics plugin's
-- host settings. Both copies run at boot, after this migration, so a real
-- DROP COLUMN here would delete the data first. The column stays in place,
-- unused, until 3.0.0.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- ssh_data.enable_docker and docker_config moved to the docker plugin's host
-- settings. The copy runs at boot, after this migration, so a real DROP
-- COLUMN here would delete the data first. The columns stay in place,
-- unused, until 3.0.0.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- The automation tables moved to the automations plugin, which renames them
-- into its own namespace at activation. That runs after this migration, so a
-- real DROP TABLE here would delete every automation first.
SELECT 1;
-8
View File
@@ -1,8 +0,0 @@
-- The ai tables moved to the ai plugin, which renames them into its own
-- namespace at activation, and ssh_data.enable_ai_assistant and the two
-- user_preferences ai columns moved to its settings. Both run after this
-- migration, so a real DROP here would delete the data first. The columns
-- stay in place, unused, until 3.0.0. The timestamp and label type changes
-- drizzle-kit also wrote are generator drift (see FINISH-LIST.md), not a
-- change to the database, and are left out on purpose.
SELECT 1;
-6
View File
@@ -1,6 +0,0 @@
-- dashboard_service_links, homepage_items and homepage_layouts moved to the
-- homepage plugin, which adopts them by rename at activation (see
-- plugins/homepage/migrations/mysql/0001_adopt_homepage_tables.sql).
-- Dropping them here would delete every row before the plugin's migration
-- ever runs, since this runs at boot before the plugins table exists.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- mac_address and wol_broadcast_address move to the wake-on-lan plugin's
-- host settings at boot, after this migration runs. Dropping them here would
-- delete the data before the copy has a chance to run.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- secret_sources moves to the secret-sources plugin's adopted table at
-- activation, after this migration runs. Dropping it here would delete the
-- data before the plugin has a chance to rename it.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- webauthn_credentials moves to the webauthn plugin's adopted table at
-- activation, and the users TOTP columns are read by the TOTP boot migration
-- after this runs. Dropping either here would lose the data first.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- sso_providers moves to the sso plugin's adopted table at activation, and
-- its LDAP rows are copied into the ldap plugin by a boot migration after
-- this runs. Dropping it here would lose the data first.
SELECT 1;
-5
View File
@@ -1,5 +0,0 @@
-- opkssh_tokens moves to the opkssh plugin's adopted table at activation,
-- and ssh_data.use_warpgate is copied into the warpgate plugin's host
-- settings by a boot migration after this runs. Dropping either here would
-- lose the data first.
SELECT 1;
@@ -1,5 +0,0 @@
-- vault_profiles and vault_tokens move to the vault plugin's adopted tables
-- at activation, and ssh_data.vault_profile_id is copied into the vault
-- plugin's host settings by a boot migration after this runs. Dropping any
-- of them here would lose the data first.
SELECT 1;
-4
View File
@@ -1,4 +0,0 @@
-- termix_identities, termix_identity_keys and termix_identity_ca move to the
-- termix-identity plugin's adopted tables at activation. Dropping them here
-- would lose the data first.
SELECT 1;
-52
View File
@@ -1,52 +0,0 @@
-- The dialect generator now decides varchar or text per table. Columns that
-- were varchar(255) only because the same name was a key in another table
-- become text, and the timestamps and labels B18's regeneration left as
-- varchar(255) on real databases follow. varchar to text keeps every value.
ALTER TABLE `api_keys` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `api_keys` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `api_keys` MODIFY COLUMN `expires_at` text;--> statement-breakpoint
ALTER TABLE `credential_access` MODIFY COLUMN `expires_at` text;--> statement-breakpoint
ALTER TABLE `credential_access` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `credential_sidebar_preferences` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `folder_access` MODIFY COLUMN `expires_at` text;--> statement-breakpoint
ALTER TABLE `folder_access` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_access` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `host_sidebar_preferences` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `name` text;--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `folder` text;--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_data` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `notification_channels` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `notification_channels` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `plugin_install_counts` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `plugin_registries` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `plugin_settings` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `plugin_storage` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `plugins` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `plugins` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `rbac_known_permissions` MODIFY COLUMN `plugin_id` text;--> statement-breakpoint
ALTER TABLE `roles` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `roles` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `sessions` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_credential_secrets` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_credential_secrets` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_auth_overrides` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_secrets` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `shared_host_secrets` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `folder` text;--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_credentials` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_folders` MODIFY COLUMN `name` text NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_folders` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `ssh_folders` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `sync_tombstones` MODIFY COLUMN `sync_id` text NOT NULL;--> statement-breakpoint
ALTER TABLE `trusted_devices` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `trusted_devices` MODIFY COLUMN `expires_at` text NOT NULL;--> statement-breakpoint
ALTER TABLE `ui_preferences` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_external_identities` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `label` text NOT NULL;--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_open_tabs` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);--> statement-breakpoint
ALTER TABLE `user_preferences` MODIFY COLUMN `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP);
@@ -1,6 +0,0 @@
-- The file manager, tunnels, web endpoint, terminal, tmux monitor, session
-- sharing and session recording host columns, and the dead show_*_in_sidebar
-- columns, moved to their plugins' host settings. The copies run after
-- plugins load, which is after this migration, so a real DROP COLUMN here
-- would delete the data first. The columns stay in place, unused, until 3.0.0.
SELECT 1;
-6
View File
@@ -1,6 +0,0 @@
-- dismissed_alerts and notification_channels moved to the alerts plugin. It
-- renames the first into its own namespace and copies the second at
-- activation, both after this migration, so a real DROP TABLE here would
-- delete them first. notification_channels stays in place, unused, until
-- 3.0.0.
SELECT 1;
@@ -1,59 +0,0 @@
CREATE TABLE `sync_conflicts` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`entity_type` text NOT NULL,
`sync_id` text NOT NULL,
`local_row` text NOT NULL,
`server_revision` int NOT NULL,
`created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `sync_conflicts_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `sync_link` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`server_url` text NOT NULL,
`server_name` text,
`server_version` text,
`session_token` text,
`custom_headers` text,
`basic_auth` text,
`allow_invalid_certificate` boolean NOT NULL DEFAULT false,
`remote_user_id` text,
`remote_username` text,
`account` text,
`scope` text,
`known_types` text,
`cursor` int NOT NULL DEFAULT 0,
`status` text NOT NULL DEFAULT ('idle'),
`last_error` text,
`linked_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
`last_sync_at` text,
CONSTRAINT `sync_link_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
CREATE TABLE `sync_records` (
`id` int AUTO_INCREMENT NOT NULL,
`user_id` varchar(255) NOT NULL,
`entity_type` varchar(255) NOT NULL,
`sync_id` varchar(255) NOT NULL,
`revision` int NOT NULL DEFAULT 0,
`seq` int NOT NULL DEFAULT 0,
`hash` text,
`deleted` boolean NOT NULL DEFAULT false,
`error` text,
`error_hash` text,
`updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
CONSTRAINT `sync_records_id` PRIMARY KEY(`id`),
CONSTRAINT `idx_sync_records_user_entity_sync` UNIQUE(`user_id`,`entity_type`,`sync_id`)
);
--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `local_only` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `ssh_data` ADD `shared_source` text;--> statement-breakpoint
ALTER TABLE `ssh_credentials` ADD `shared_source` text;--> statement-breakpoint
ALTER TABLE `ssh_folders` ADD `local_only` boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE `sync_conflicts` ADD CONSTRAINT `sync_conflicts_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `sync_link` ADD CONSTRAINT `sync_link_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE `sync_records` ADD CONSTRAINT `sync_records_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX `idx_sync_conflicts_user` ON `sync_conflicts` (`user_id`);--> statement-breakpoint
CREATE INDEX `idx_sync_records_user_seq` ON `sync_records` (`user_id`,`seq`);
-1
View File
@@ -1 +0,0 @@
DROP TABLE `sync_tombstones`;
@@ -1 +0,0 @@
ALTER TABLE `plugin_settings` MODIFY COLUMN `scope` enum('admin','user','host','secret') NOT NULL;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE `ssh_data` ADD `ssh_options` text;
Loaded 100 of 1884 files, more files were not shown because too many files have changed in this diff. Show more