Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
+3 |
a64c956c5b | ||
|
|
188380e8e7 | ||
|
|
9effddaba8 | ||
|
|
ad0e62434b | ||
|
|
d831b46cb3 | ||
|
|
4a7117b67f | ||
|
|
9ae48a8c6c | ||
|
|
e5b0db60c4 | ||
|
|
0327f9020c | ||
|
|
9f066d814a | ||
|
+2 |
1a26628a48 | ||
|
|
cf3e2cb499 | ||
|
|
845bb494a6 | ||
|
|
1b3a805010 | ||
|
|
b2d1441164 | ||
|
+7 |
ddbdd5c437 | ||
|
|
fba645e92e | ||
|
|
a6d0658e41 | ||
|
|
1aea3603a7 | ||
|
|
972e27eb64 | ||
|
|
38e128bd16 | ||
|
|
0712fdd731 | ||
|
|
bb5559c696 | ||
|
|
b2ff35e106 | ||
|
|
19a2cb8eed | ||
|
|
078e6d5de0 | ||
|
|
794714368a | ||
|
|
4fbaf50e08 | ||
|
|
4ec4cfcdb7 | ||
|
|
a46f2f1343 | ||
|
|
72e5ff5a64 | ||
|
|
63cfdfda9e | ||
|
|
3a3b51d1ae | ||
|
|
3f4280c2ff | ||
|
|
97124bc3b6 | ||
|
|
253be0077e | ||
|
|
fe96e68872 | ||
|
|
30acbed1a7 | ||
|
|
7416734f68 | ||
|
|
9de904dd4c | ||
|
|
fd27a366d0 | ||
|
|
eb49f197ca | ||
|
|
98195ec5c3 | ||
|
|
9c317251ca | ||
|
|
6194a58b1a | ||
|
|
b1ec2bcd2b | ||
|
|
0354401640 | ||
|
|
d1a8dcf3c6 | ||
|
|
8072b4ede9 | ||
|
|
688e662042 | ||
|
|
612ba046a8 | ||
|
|
27d613e50c | ||
|
|
b02e9876ae | ||
|
|
e2db514173 | ||
|
+2 |
ac24fdcba8 | ||
|
|
580c284065 | ||
|
|
2d5da0439e | ||
|
|
4d04559ca5 | ||
|
|
f0f3e0b063 | ||
|
|
c3282b5dca | ||
|
|
dccaae07ab | ||
|
|
52f4e51ae0 | ||
|
|
da79b01db4 | ||
|
|
bc386b1247 | ||
|
|
dd008e4d6b | ||
|
|
7370e8f3df | ||
|
|
b5ab1479ce | ||
|
|
c060d668e0 | ||
|
|
5777351145 | ||
|
|
33dcde0827 | ||
|
|
eaa758effe | ||
|
|
557a789a6c | ||
|
|
d2e13cdfd8 | ||
|
|
dc79d170b6 | ||
|
|
604de8a683 | ||
|
|
3b465a0747 | ||
|
|
f803fc83a4 |
@@ -34,7 +34,7 @@ README.md
|
||||
CONTRIBUTING.md
|
||||
LICENSE
|
||||
|
||||
repo-images/
|
||||
docs/repo-images/
|
||||
|
||||
uploads/
|
||||
|
||||
|
||||
@@ -5,4 +5,4 @@ contact_links:
|
||||
about: Report any feature requests or bugs in the support center
|
||||
- name: Discord
|
||||
url: https://discord.gg/jVQGdvHDrf
|
||||
about: Official Termix Discord server for general discussion and quick support
|
||||
about: Official Termix Discord server for general discussion (not recommended for support)
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
version: 2
|
||||
updates:
|
||||
# npm dependencies (single root package.json, no workspaces)
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
open-pull-requests-limit: 15
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "npm"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
prefix-development: "chore"
|
||||
include: "scope"
|
||||
groups:
|
||||
dev-patch-updates:
|
||||
dependency-type: "development"
|
||||
@@ -21,20 +31,61 @@ updates:
|
||||
dependency-type: "production"
|
||||
update-types:
|
||||
- "minor"
|
||||
# Major bumps grouped so they land as a single reviewable PR instead of
|
||||
# one-per-package noise. These often need manual follow-up (Electron,
|
||||
# React, Vite, Tailwind, Express 5, etc.).
|
||||
major-updates:
|
||||
update-types:
|
||||
- "major"
|
||||
ignore:
|
||||
# typescript-eslint declares `typescript: >=4.8.4 <6.1.0`, and TypeScript 7
|
||||
# removed `ts.Extension`, which @typescript-eslint/typescript-estree reads
|
||||
# at import time. Bumping to 7 makes `eslint .` fail to load its own config,
|
||||
# so `npm run lint` cannot run at all. Drop this once typescript-eslint
|
||||
# supports TypeScript 7.
|
||||
- dependency-name: "typescript"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
# Docker base images (docker/Dockerfile + docker-compose / compose-dev)
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/docker"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
groups:
|
||||
patch-updates:
|
||||
docker-patch-updates:
|
||||
update-types:
|
||||
- "patch"
|
||||
minor-updates:
|
||||
docker-minor-updates:
|
||||
update-types:
|
||||
- "minor"
|
||||
docker-major-updates:
|
||||
update-types:
|
||||
- "major"
|
||||
|
||||
# GitHub Actions used across the workflows in .github/workflows
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "github-actions"
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
include: "scope"
|
||||
groups:
|
||||
github-actions:
|
||||
update-types:
|
||||
- "patch"
|
||||
- "minor"
|
||||
- "major"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
custom: https://donate.termix.site/
|
||||
@@ -0,0 +1,179 @@
|
||||
name: Weekly Beta Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "15 6 * * 1"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: "Build and test but do not push images, upload installers, or publish a release"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
prep:
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
outputs:
|
||||
dev_branch: ${{ steps.dev.outputs.branch }}
|
||||
beta_version: ${{ steps.dev.outputs.beta_version }}
|
||||
sha: ${{ steps.dev.outputs.sha }}
|
||||
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: Resolve newest dev branch and compute beta version
|
||||
id: dev
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
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 "Newest dev branch: $DEV_BRANCH"
|
||||
else
|
||||
echo "No dev-X.Y.Z branch open; nothing to snapshot for this week's beta."
|
||||
echo "branch=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BASE_VERSION=$(node scripts/parse-dev-branch.cjs "$DEV_BRANCH")
|
||||
BETA_VERSION="${BASE_VERSION}-beta.$(date -u +%Y%m%d)"
|
||||
SHA=$(gh api "repos/${{ github.repository }}/branches/$DEV_BRANCH" -q .commit.sha)
|
||||
|
||||
echo "Beta version: $BETA_VERSION"
|
||||
echo "branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
|
||||
echo "beta_version=$BETA_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
verify:
|
||||
needs: [prep]
|
||||
if: ${{ needs.prep.outputs.dev_branch != '' }}
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout dev branch
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ needs.prep.outputs.dev_branch }}
|
||||
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
|
||||
|
||||
- name: Run ESLint
|
||||
run: npx eslint .
|
||||
|
||||
- name: Run Prettier check
|
||||
run: npx prettier --check .
|
||||
|
||||
- name: Type check
|
||||
run: npx tsc --noEmit
|
||||
|
||||
- name: Run unit tests
|
||||
run: npm run test
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
create-release:
|
||||
needs: [prep, verify]
|
||||
if: ${{ needs.prep.outputs.dev_branch != '' && inputs.dry_run != true }}
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout dev branch
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ needs.prep.outputs.dev_branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve previous beta commit
|
||||
id: prev
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
PREV_SHA=$(gh release view beta --repo ${{ github.repository }} --json targetCommitish -q .targetCommitish 2>/dev/null || true)
|
||||
if [ -n "$PREV_SHA" ] && git cat-file -e "$PREV_SHA" 2>/dev/null && git merge-base --is-ancestor "$PREV_SHA" "${{ needs.prep.outputs.sha }}"; then
|
||||
echo "sha=$PREV_SHA" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "sha=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Generate rolling beta release notes
|
||||
run: |
|
||||
if [ -n "${{ steps.prev.outputs.sha }}" ]; then
|
||||
CHANGES=$(git log --oneline --no-merges "${{ steps.prev.outputs.sha }}..${{ needs.prep.outputs.sha }}" -- . ':!package-lock.json' | sed 's/^/- /')
|
||||
fi
|
||||
if [ -z "$CHANGES" ]; then
|
||||
CHANGES="- No new commits since the last beta."
|
||||
fi
|
||||
|
||||
cat > BETA_RELEASE_BODY.md << EOF
|
||||
> [!WARNING]
|
||||
> This is an automated weekly beta build, snapshotted from the \`${{ needs.prep.outputs.dev_branch }}\` branch. It is not a stable release: it may contain unfinished features, regressions, or breaking changes, and this tag is overwritten every week. Do not run it in production.
|
||||
>
|
||||
> Found a bug? [Open a Beta Feedback report](https://github.com/Termix-SSH/Support/issues/new?template=beta_feedback.yml) and mention this build: \`${{ needs.prep.outputs.beta_version }}\`.
|
||||
|
||||
**Snapshot of:** \`${{ needs.prep.outputs.dev_branch }}\` @ \`${{ needs.prep.outputs.sha }}\`
|
||||
**Docker image:** \`ghcr.io/lukegus/termix:beta\` / \`docker.io/bugattiguy527/termix:beta\` (rolling), or pin to \`:beta-${{ needs.prep.outputs.beta_version }}\` for this exact build.
|
||||
**Built:** $(date -u +"%Y-%m-%d %H:%M UTC")
|
||||
|
||||
### Changes since last beta
|
||||
|
||||
$CHANGES
|
||||
EOF
|
||||
|
||||
- name: Create or update rolling beta release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
TAG="beta"
|
||||
TITLE="Beta (rolling) - ${{ needs.prep.outputs.beta_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 BETA_RELEASE_BODY.md \
|
||||
--prerelease --target "${{ needs.prep.outputs.sha }}"
|
||||
else
|
||||
gh release create "$TAG" --repo ${{ github.repository }} \
|
||||
--title "$TITLE" --notes-file BETA_RELEASE_BODY.md \
|
||||
--prerelease --target "${{ needs.prep.outputs.sha }}"
|
||||
fi
|
||||
|
||||
docker:
|
||||
needs: [prep, verify, create-release]
|
||||
if: ${{ always() && needs.prep.outputs.dev_branch != '' && needs.verify.result == 'success' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped') }}
|
||||
uses: ./.github/workflows/docker.yml
|
||||
with:
|
||||
version: ${{ needs.prep.outputs.beta_version }}
|
||||
build_type: Beta
|
||||
dry_run: ${{ inputs.dry_run == true }}
|
||||
source_ref: ${{ needs.prep.outputs.sha }}
|
||||
secrets: inherit
|
||||
|
||||
electron-release:
|
||||
needs: [prep, verify, create-release]
|
||||
if: ${{ always() && needs.prep.outputs.dev_branch != '' && inputs.dry_run != true && needs.create-release.result == 'success' }}
|
||||
uses: ./.github/workflows/electron.yml
|
||||
with:
|
||||
build_type: all
|
||||
artifact_destination: release
|
||||
release_tag: beta
|
||||
version_override: ${{ needs.prep.outputs.beta_version }}
|
||||
source_ref: ${{ needs.prep.outputs.sha }}
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,83 @@
|
||||
name: Crowdin Sync
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch to sync translations into"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
crowdin:
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Resolve target branch
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH="${{ inputs.branch }}"
|
||||
if [ -z "$BRANCH" ]; then
|
||||
BRANCH="${{ github.event.repository.default_branch }}"
|
||||
fi
|
||||
echo "name=$BRANCH" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ steps.branch.outputs.name }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GHCR_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Upload sources to Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: false
|
||||
create_pull_request: false
|
||||
push_translations: false
|
||||
token: ${{ secrets.CROWDIN_API_KEY }}
|
||||
project_id: "858252"
|
||||
env:
|
||||
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }}
|
||||
|
||||
- name: Machine pre-translate untranslated strings
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: node scripts/crowdin-pretranslate.cjs
|
||||
|
||||
- name: Download translations from Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
create_pull_request: false
|
||||
push_translations: false
|
||||
token: ${{ secrets.CROWDIN_API_KEY }}
|
||||
project_id: "858252"
|
||||
env:
|
||||
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }}
|
||||
|
||||
- name: Commit translations
|
||||
run: |
|
||||
git config user.name "LukeGus"
|
||||
git config user.email "bugattiguy527@gmail.com"
|
||||
|
||||
git add src/ui/locales/translated
|
||||
if git diff --cached --quiet; then
|
||||
echo "No translation changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "chore: sync Crowdin translations"
|
||||
git push origin HEAD:"${{ steps.branch.outputs.name }}"
|
||||
@@ -0,0 +1,157 @@
|
||||
name: Retarget and Merge Dependabot PRs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
retarget-and-merge:
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Resolve newest dev branch
|
||||
id: dev
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
REFS=$(gh api "repos/${{ github.repository }}/branches" --paginate -q '.[].name')
|
||||
# The helper exits non-zero when no dev-X.Y.Z branch exists; treat that
|
||||
# as "nothing to do" rather than a workflow failure.
|
||||
if DEV_BRANCH=$(printf '%s\n' "$REFS" | node scripts/latest-dev-branch.cjs 2>/dev/null); then
|
||||
echo "Newest dev branch: $DEV_BRANCH"
|
||||
echo "branch=$DEV_BRANCH" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No dev-X.Y.Z branch open; nothing to retarget."
|
||||
echo "branch=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Retarget and merge Dependabot PRs
|
||||
if: ${{ steps.dev.outputs.branch != '' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
DEV_BRANCH: ${{ steps.dev.outputs.branch }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
|
||||
CONFLICT_LABEL="dependabot-rebase-requested"
|
||||
|
||||
# Ensure the bookkeeping label exists (no-op if it already does).
|
||||
gh label create "$CONFLICT_LABEL" --repo "$REPO" \
|
||||
--color "D93F0B" --description "Retarget workflow asked Dependabot to rebase a conflicting PR" \
|
||||
2>/dev/null || true
|
||||
|
||||
# True if the PR already carries the conflict label.
|
||||
has_conflict_label() {
|
||||
gh pr view "$1" --repo "$REPO" --json labels \
|
||||
-q '.labels[].name' | grep -qx "$CONFLICT_LABEL"
|
||||
}
|
||||
|
||||
# Wait until GitHub has a definite mergeable verdict for a PR (it
|
||||
# returns UNKNOWN while recomputing after a base change or a push).
|
||||
# Echoes "<mergeable> <mergeStateStatus>".
|
||||
wait_for_verdict() {
|
||||
local pr="$1" mergeable state
|
||||
for _ in $(seq 1 30); do
|
||||
read -r mergeable state < <(gh pr view "$pr" --repo "$REPO" \
|
||||
--json mergeable,mergeStateStatus \
|
||||
-q '.mergeable + " " + .mergeStateStatus')
|
||||
if [ "$mergeable" != "UNKNOWN" ] && [ "$state" != "UNKNOWN" ]; then
|
||||
echo "$mergeable $state"
|
||||
return 0
|
||||
fi
|
||||
sleep 20
|
||||
done
|
||||
echo "$mergeable $state"
|
||||
}
|
||||
|
||||
# Phase 1: retarget every open Dependabot PR from main onto the dev
|
||||
# branch. This kicks off a Dependabot rebase for each.
|
||||
PR_NUMBERS=$(gh pr list --repo "$REPO" \
|
||||
--author "app/dependabot" \
|
||||
--base main \
|
||||
--state open \
|
||||
--json number -q '.[].number')
|
||||
|
||||
# Pick up PRs already sitting on the dev branch from a previous run too.
|
||||
PR_NUMBERS="$PR_NUMBERS $(gh pr list --repo "$REPO" \
|
||||
--author "app/dependabot" \
|
||||
--base "$DEV_BRANCH" \
|
||||
--state open \
|
||||
--json number -q '.[].number')"
|
||||
|
||||
PR_NUMBERS=$(printf '%s\n' $PR_NUMBERS | sort -un)
|
||||
|
||||
if [ -z "$PR_NUMBERS" ]; then
|
||||
echo "No open Dependabot PRs to process."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for PR in $PR_NUMBERS; do
|
||||
BASE=$(gh pr view "$PR" --repo "$REPO" --json baseRefName -q .baseRefName)
|
||||
if [ "$BASE" != "$DEV_BRANCH" ]; then
|
||||
echo "Retargeting PR #$PR ($BASE -> $DEV_BRANCH)"
|
||||
gh pr edit "$PR" --repo "$REPO" --base "$DEV_BRANCH"
|
||||
fi
|
||||
done
|
||||
|
||||
# Phase 2: merge one at a time. Each merge can make the remaining npm
|
||||
# PRs stale, so re-check immediately before merging and rebase stragglers.
|
||||
for PR in $PR_NUMBERS; do
|
||||
echo "::group::PR #$PR"
|
||||
|
||||
read -r MERGEABLE STATE < <(wait_for_verdict "$PR")
|
||||
echo " mergeable=$MERGEABLE mergeStateStatus=$STATE"
|
||||
|
||||
# BEHIND = clean but needs the latest base; ask Dependabot to rebase
|
||||
# and skip for now (next run merges it once it is up to date).
|
||||
if [ "$STATE" = "BEHIND" ]; then
|
||||
echo " PR #$PR is behind $DEV_BRANCH; asking Dependabot to rebase."
|
||||
gh pr comment "$PR" --repo "$REPO" --body "@dependabot rebase"
|
||||
echo "::endgroup::"
|
||||
continue
|
||||
fi
|
||||
|
||||
# DIRTY / CONFLICTING = a real conflict. Try a rebase once (label it so
|
||||
# we can tell next time); if it is STILL conflicting on a later run
|
||||
# despite already being labelled, the rebase failed for good - close it
|
||||
# so Dependabot reopens a fresh PR against the current dev branch.
|
||||
if [ "$MERGEABLE" = "CONFLICTING" ] || [ "$STATE" = "DIRTY" ]; then
|
||||
if has_conflict_label "$PR"; then
|
||||
echo " PR #$PR still conflicts after a prior rebase request; closing so Dependabot reopens it fresh."
|
||||
gh pr close "$PR" --repo "$REPO" --delete-branch \
|
||||
--comment "Closing: this PR still conflicts with $DEV_BRANCH after a rebase attempt (its changes are likely already merged). Dependabot will reopen a fresh PR computed against the current $DEV_BRANCH."
|
||||
else
|
||||
echo " PR #$PR conflicts with $DEV_BRANCH; requesting a rebase and labelling it."
|
||||
gh pr edit "$PR" --repo "$REPO" --add-label "$CONFLICT_LABEL"
|
||||
gh pr comment "$PR" --repo "$REPO" --body "@dependabot rebase"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
continue
|
||||
fi
|
||||
|
||||
# A clean PR that was previously flagged has recovered - drop the label.
|
||||
if has_conflict_label "$PR"; then
|
||||
gh pr edit "$PR" --repo "$REPO" --remove-label "$CONFLICT_LABEL" || true
|
||||
fi
|
||||
|
||||
echo " Squash-merging PR #$PR"
|
||||
if gh pr merge "$PR" --repo "$REPO" --squash --admin; then
|
||||
echo " Merged PR #$PR"
|
||||
# Give GitHub a moment to mark the now-stale siblings BEHIND.
|
||||
sleep 15
|
||||
else
|
||||
echo " Could not merge PR #$PR now; it will be retried next run."
|
||||
fi
|
||||
|
||||
echo "::endgroup::"
|
||||
done
|
||||
@@ -13,36 +13,72 @@ on:
|
||||
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., 1.8.0)"
|
||||
required: true
|
||||
type: string
|
||||
build_type:
|
||||
description: "Build type (Development 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@v5
|
||||
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"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: useblacksmith/setup-docker-builder@v2
|
||||
|
||||
- name: Determine tags
|
||||
id: tags
|
||||
run: |
|
||||
VERSION=${{ github.event.inputs.version }}
|
||||
BUILD_TYPE=${{ github.event.inputs.build_type }}
|
||||
VERSION=${{ inputs.version }}
|
||||
BUILD_TYPE=${{ inputs.build_type }}
|
||||
|
||||
TAGS=()
|
||||
ALL_TAGS=()
|
||||
|
||||
if [ "$BUILD_TYPE" = "Production" ]; then
|
||||
TAGS+=("release-$VERSION" "latest")
|
||||
TAGS+=("release-$VERSION" "$VERSION" "latest")
|
||||
for tag in "${TAGS[@]}"; do
|
||||
ALL_TAGS+=("ghcr.io/lukegus/termix:$tag")
|
||||
ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag")
|
||||
done
|
||||
elif [ "$BUILD_TYPE" = "Beta" ]; then
|
||||
TAGS+=("beta" "beta-$VERSION")
|
||||
for tag in "${TAGS[@]}"; do
|
||||
ALL_TAGS+=("ghcr.io/lukegus/termix:$tag")
|
||||
ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag")
|
||||
@@ -57,35 +93,37 @@ jobs:
|
||||
echo "ALL_TAGS=$(IFS=,; echo "${ALL_TAGS[*]}")" >> $GITHUB_ENV
|
||||
|
||||
- name: Login to GHCR
|
||||
if: ${{ !inputs.dry_run }}
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: lukegus
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
|
||||
- name: Login to Docker Hub (prod only)
|
||||
if: ${{ github.event.inputs.build_type == 'Production' }}
|
||||
- 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_TOKEN }}
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@v7
|
||||
uses: useblacksmith/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
push: true
|
||||
push: ${{ !inputs.dry_run }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ env.ALL_TAGS }}
|
||||
build-args: |
|
||||
BUILDKIT_INLINE_CACHE=1
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||
labels: |
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.revision=${{ env.SOURCE_SHA }}
|
||||
org.opencontainers.image.created=${{ github.run_id }}
|
||||
outputs: type=registry,compression=gzip,compression-level=9
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
outputs: ${{ inputs.dry_run && 'type=cacheonly' || 'type=registry,compression=zstd' }}
|
||||
|
||||
- name: Cleanup Docker
|
||||
if: always()
|
||||
|
||||
@@ -23,48 +23,70 @@ on:
|
||||
- file
|
||||
- release
|
||||
- submit
|
||||
source_ref:
|
||||
description: "Git ref/SHA to build (defaults to the workflow ref)"
|
||||
required: false
|
||||
default: ""
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
description: "Platform to build for (all, windows, linux, macos)"
|
||||
required: true
|
||||
type: string
|
||||
artifact_destination:
|
||||
description: "What to do with the built app (none, file, release, submit)"
|
||||
required: true
|
||||
type: string
|
||||
release_tag:
|
||||
description: "Explicit release tag to upload assets to (defaults to latest release when empty)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
version_override:
|
||||
description: "Version string to stamp into built artifacts instead of package.json's version"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
source_ref:
|
||||
description: "Git ref/SHA to build"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
outputs:
|
||||
macos_universal_dmg_sha256:
|
||||
description: "SHA256 of the universal macOS DMG (for Homebrew cask)"
|
||||
value: ${{ jobs.build-macos.outputs.dmg_sha256 }}
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
if: (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'windows' || github.event.inputs.build_type == '') && github.event.inputs.artifact_destination != 'submit'
|
||||
runs-on: blacksmith-4vcpu-windows-2025
|
||||
if: (inputs.build_type == 'all' || inputs.build_type == 'windows' || inputs.build_type == '') && inputs.artifact_destination != 'submit'
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
$maxAttempts = 3
|
||||
$attempt = 1
|
||||
while ($attempt -le $maxAttempts) {
|
||||
try {
|
||||
npm ci
|
||||
break
|
||||
} catch {
|
||||
if ($attempt -eq $maxAttempts) {
|
||||
Write-Error "npm ci failed after $maxAttempts attempts"
|
||||
exit 1
|
||||
}
|
||||
Start-Sleep -Seconds 10
|
||||
$attempt++
|
||||
}
|
||||
}
|
||||
run: npm ci
|
||||
|
||||
- name: Get version
|
||||
id: package-version
|
||||
run: |
|
||||
$VERSION = (Get-Content package.json | ConvertFrom-Json).version
|
||||
$VERSION = "${{ inputs.version_override }}"
|
||||
if ([string]::IsNullOrEmpty($VERSION)) {
|
||||
$VERSION = (Get-Content package.json | ConvertFrom-Json).version
|
||||
}
|
||||
echo "version=$VERSION" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Build Windows (All Architectures)
|
||||
@@ -73,32 +95,32 @@ jobs:
|
||||
run: npm run build && npx electron-builder --win --x64 --ia32
|
||||
|
||||
- name: Upload Windows x64 NSIS Installer
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_x64_nsis
|
||||
path: release/termix_windows_x64_nsis.exe
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Windows ia32 NSIS Installer
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_windows_ia32_nsis.exe') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_windows_ia32_nsis.exe') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_ia32_nsis
|
||||
path: release/termix_windows_ia32_nsis.exe
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Windows x64 MSI Installer
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_windows_x64_msi.msi') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_windows_x64_msi.msi') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_x64_msi
|
||||
path: release/termix_windows_x64_msi.msi
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Windows ia32 MSI Installer
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_windows_ia32_msi.msi') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_windows_ia32_msi.msi') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_ia32_msi
|
||||
path: release/termix_windows_ia32_msi.msi
|
||||
@@ -115,57 +137,48 @@ jobs:
|
||||
Compress-Archive -Path "release\win-ia32-unpacked\*" -DestinationPath "termix_windows_ia32_portable.zip"
|
||||
|
||||
- name: Upload Windows x64 Portable
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('termix_windows_x64_portable.zip') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('termix_windows_x64_portable.zip') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_x64_portable
|
||||
path: termix_windows_x64_portable.zip
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Windows ia32 Portable
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('termix_windows_ia32_portable.zip') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('termix_windows_ia32_portable.zip') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_windows_ia32_portable
|
||||
path: termix_windows_ia32_portable.zip
|
||||
retention-days: 30
|
||||
|
||||
build-linux:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'linux' || github.event.inputs.build_type == '') && github.event.inputs.artifact_destination != 'submit'
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
if: (inputs.build_type == 'all' || inputs.build_type == 'linux' || inputs.build_type == '') && inputs.artifact_destination != 'submit'
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install system dependencies for AppImage
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libfuse2
|
||||
sudo apt-get install -y libfuse2 flatpak flatpak-builder imagemagick
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
for i in 1 2 3;
|
||||
do
|
||||
if npm ci; then
|
||||
break
|
||||
else
|
||||
if [ $i -eq 3 ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
npm ci
|
||||
npm install --force @rollup/rollup-linux-x64-gnu
|
||||
npm install --force @rollup/rollup-linux-arm64-gnu
|
||||
npm install --force @rollup/rollup-linux-arm-gnueabihf
|
||||
@@ -196,82 +209,77 @@ jobs:
|
||||
cd ..
|
||||
|
||||
- name: Upload Linux x64 AppImage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_x64_appimage
|
||||
path: release/termix_linux_x64_appimage.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux arm64 AppImage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_arm64_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_arm64_appimage.AppImage') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_arm64_appimage
|
||||
path: release/termix_linux_arm64_appimage.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux armv7l AppImage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_armv7l_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_armv7l_appimage.AppImage') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_armv7l_appimage
|
||||
path: release/termix_linux_armv7l_appimage.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux x64 DEB
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_x64_deb.deb') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_x64_deb.deb') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_x64_deb
|
||||
path: release/termix_linux_x64_deb.deb
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux arm64 DEB
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_arm64_deb.deb') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_arm64_deb.deb') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_arm64_deb
|
||||
path: release/termix_linux_arm64_deb.deb
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux armv7l DEB
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_armv7l_deb.deb') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_armv7l_deb.deb') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_armv7l_deb
|
||||
path: release/termix_linux_armv7l_deb.deb
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux x64 tar.gz
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_x64_portable.tar.gz') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_x64_portable.tar.gz') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_x64_portable
|
||||
path: release/termix_linux_x64_portable.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux arm64 tar.gz
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_arm64_portable.tar.gz') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_arm64_portable.tar.gz') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_arm64_portable
|
||||
path: release/termix_linux_arm64_portable.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Linux armv7l tar.gz
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_armv7l_portable.tar.gz') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_armv7l_portable.tar.gz') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_armv7l_portable
|
||||
path: release/termix_linux_armv7l_portable.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
- name: Install Flatpak builder and dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y flatpak flatpak-builder imagemagick
|
||||
|
||||
- name: Add Flathub repository
|
||||
run: |
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
@@ -285,7 +293,10 @@ jobs:
|
||||
- name: Get version for Flatpak
|
||||
id: flatpak-version
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
VERSION="${{ inputs.version_override }}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
fi
|
||||
RELEASE_DATE=$(date +%Y-%m-%d)
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT
|
||||
@@ -299,9 +310,9 @@ jobs:
|
||||
CHECKSUM_ARM64=$(sha256sum "release/termix_linux_arm64_appimage.AppImage" | awk '{print $1}')
|
||||
|
||||
mkdir -p flatpak-build
|
||||
cp flatpak/com.karmaa.termix.yml flatpak-build/
|
||||
cp flatpak/com.karmaa.termix.desktop flatpak-build/
|
||||
cp flatpak/com.karmaa.termix.metainfo.xml flatpak-build/
|
||||
cp packaging/flatpak/com.karmaa.termix.yml flatpak-build/
|
||||
cp packaging/flatpak/com.karmaa.termix.desktop flatpak-build/
|
||||
cp packaging/flatpak/com.karmaa.termix.metainfo.xml flatpak-build/
|
||||
cp public/icon.svg flatpak-build/com.karmaa.termix.svg
|
||||
convert public/icon.png -resize 256x256 flatpak-build/icon-256.png
|
||||
convert public/icon.png -resize 128x128 flatpak-build/icon-128.png
|
||||
@@ -333,57 +344,50 @@ jobs:
|
||||
- name: Create flatpakref file
|
||||
run: |
|
||||
VERSION="${{ steps.flatpak-version.outputs.version }}"
|
||||
cp flatpak/com.karmaa.termix.flatpakref release/
|
||||
cp packaging/flatpak/com.karmaa.termix.flatpakref release/
|
||||
sed -i "s|VERSION_PLACEHOLDER|release-${VERSION}-tag|g" release/com.karmaa.termix.flatpakref
|
||||
|
||||
- name: Upload Flatpak bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_linux_flatpak.flatpak') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_linux_flatpak.flatpak') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_flatpak
|
||||
path: release/termix_linux_flatpak.flatpak
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Flatpakref
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/com.karmaa.termix.flatpakref') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/com.karmaa.termix.flatpakref') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_linux_flatpakref
|
||||
path: release/com.karmaa.termix.flatpakref
|
||||
retention-days: 30
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
if: (github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == 'all') && github.event.inputs.artifact_destination != 'submit'
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
if: (inputs.build_type == 'macos' || inputs.build_type == 'all') && inputs.artifact_destination != 'submit'
|
||||
needs: []
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
dmg_sha256: ${{ steps.dmg-checksum.outputs.sha256 }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
for i in 1 2 3;
|
||||
do
|
||||
if npm ci; then
|
||||
break
|
||||
else
|
||||
if [ $i -eq 3 ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
npm ci
|
||||
npm install --force @rollup/rollup-darwin-arm64
|
||||
npm install dmg-license
|
||||
|
||||
@@ -490,16 +494,16 @@ jobs:
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [ "${{ steps.check_certs.outputs.has_certs }}" != "true" ]; then
|
||||
npm run build
|
||||
fi
|
||||
export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
||||
npx electron-builder --mac dmg --universal --x64 --arm64 --publish never
|
||||
|
||||
- name: Upload macOS MAS PKG
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && hashFiles('release/termix_macos_universal_mas.pkg') != '' && (github.event.inputs.artifact_destination == 'file' || github.event.inputs.artifact_destination == 'release' || github.event.inputs.artifact_destination == 'submit')
|
||||
uses: actions/upload-artifact@v4
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && hashFiles('release/termix_macos_universal_mas.pkg') != '' && (inputs.artifact_destination == 'file' || inputs.artifact_destination == 'release' || inputs.artifact_destination == 'submit')
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: termix_macos_universal_mas
|
||||
path: release/termix_macos_universal_mas.pkg
|
||||
@@ -507,24 +511,24 @@ jobs:
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload macOS Universal DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_macos_universal_dmg
|
||||
path: release/termix_macos_universal_dmg.dmg
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload macOS x64 DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_macos_x64_dmg.dmg') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_macos_x64_dmg.dmg') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_macos_x64_dmg
|
||||
path: release/termix_macos_x64_dmg.dmg
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload macOS arm64 DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('release/termix_macos_arm64_dmg.dmg') != '' && github.event.inputs.artifact_destination != 'none'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('release/termix_macos_arm64_dmg.dmg') != '' && inputs.artifact_destination != 'none'
|
||||
with:
|
||||
name: termix_macos_arm64_dmg
|
||||
path: release/termix_macos_arm64_dmg.dmg
|
||||
@@ -533,11 +537,21 @@ jobs:
|
||||
- name: Get version for Homebrew
|
||||
id: homebrew-version
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
VERSION="${{ inputs.version_override }}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Compute universal DMG checksum
|
||||
id: dmg-checksum
|
||||
if: hashFiles('release/termix_macos_universal_dmg.dmg') != ''
|
||||
run: |
|
||||
CHECKSUM=$(shasum -a 256 "release/termix_macos_universal_dmg.dmg" | awk '{print $1}')
|
||||
echo "sha256=$CHECKSUM" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate Homebrew Cask
|
||||
if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && (github.event.inputs.artifact_destination == 'file' || github.event.inputs.artifact_destination == 'release')
|
||||
if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && inputs.version_override == '' && (inputs.artifact_destination == 'file' || inputs.artifact_destination == 'release')
|
||||
run: |
|
||||
VERSION="${{ steps.homebrew-version.outputs.version }}"
|
||||
DMG_PATH="release/termix_macos_universal_dmg.dmg"
|
||||
@@ -554,15 +568,15 @@ jobs:
|
||||
sed -i '' "s|release-[0-9.]*-tag|release-$VERSION-tag|g" homebrew-generated/termix.rb
|
||||
|
||||
- name: Upload Homebrew Cask as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'file'
|
||||
uses: actions/upload-artifact@v7
|
||||
if: hashFiles('homebrew-generated/termix.rb') != '' && inputs.artifact_destination == 'file'
|
||||
with:
|
||||
name: termix_macos_homebrew_cask
|
||||
path: homebrew-generated/termix.rb
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload Homebrew Cask to release
|
||||
if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'release'
|
||||
if: hashFiles('homebrew-generated/termix.rb') != '' && inputs.version_override == '' && inputs.artifact_destination == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
@@ -583,15 +597,16 @@ jobs:
|
||||
security delete-keychain $RUNNER_TEMP/dev-signing.keychain-db || true
|
||||
|
||||
submit-to-chocolatey:
|
||||
runs-on: windows-latest
|
||||
if: github.event.inputs.artifact_destination == 'submit' && (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'windows' || github.event.inputs.build_type == '')
|
||||
runs-on: blacksmith-4vcpu-windows-2025
|
||||
if: inputs.artifact_destination == 'submit' && (inputs.build_type == 'all' || inputs.build_type == 'windows' || inputs.build_type == '')
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Get version from package.json
|
||||
@@ -631,7 +646,7 @@ jobs:
|
||||
$DOWNLOAD_URL = "https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$MSI_NAME"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path "choco-build"
|
||||
Copy-Item -Path "chocolatey\*" -Destination "choco-build" -Recurse -Force
|
||||
Copy-Item -Path "packaging\chocolatey\*" -Destination "choco-build" -Recurse -Force
|
||||
|
||||
$installScript = Get-Content "choco-build\tools\chocolateyinstall.ps1" -Raw -Encoding UTF8
|
||||
$installScript = $installScript -replace 'DOWNLOAD_URL_PLACEHOLDER', $DOWNLOAD_URL
|
||||
@@ -681,23 +696,24 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Upload Chocolatey package as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: chocolatey-package
|
||||
path: choco-build/*.nupkg
|
||||
retention-days: 30
|
||||
|
||||
submit-to-flatpak:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: github.event.inputs.artifact_destination == 'submit' && (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'linux' || github.event.inputs.build_type == '')
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
if: inputs.artifact_destination == 'submit' && (inputs.build_type == 'all' || inputs.build_type == 'linux' || inputs.build_type == '')
|
||||
needs: []
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Get version from package.json
|
||||
@@ -735,7 +751,7 @@ jobs:
|
||||
echo "appimage_arm64_name=$APPIMAGE_ARM64_NAME" >> $GITHUB_OUTPUT
|
||||
echo "checksum_arm64=$CHECKSUM_ARM64" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install ImageMagick for icon generation
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y imagemagick
|
||||
@@ -746,15 +762,13 @@ jobs:
|
||||
CHECKSUM_X64="${{ steps.appimage-info.outputs.checksum_x64 }}"
|
||||
CHECKSUM_ARM64="${{ steps.appimage-info.outputs.checksum_arm64 }}"
|
||||
RELEASE_DATE="${{ steps.package-version.outputs.release_date }}"
|
||||
APPIMAGE_X64_NAME="${{ steps.appimage-info.outputs.appimage_x64_name }}"
|
||||
APPIMAGE_ARM64_NAME="${{ steps.appimage-info.outputs.appimage_arm64_name }}"
|
||||
|
||||
mkdir -p flatpak-submission
|
||||
|
||||
cp flatpak/com.karmaa.termix.yml flatpak-submission/
|
||||
cp flatpak/com.karmaa.termix.desktop flatpak-submission/
|
||||
cp flatpak/com.karmaa.termix.metainfo.xml flatpak-submission/
|
||||
cp flatpak/flathub.json flatpak-submission/
|
||||
cp packaging/flatpak/com.karmaa.termix.yml flatpak-submission/
|
||||
cp packaging/flatpak/com.karmaa.termix.desktop flatpak-submission/
|
||||
cp packaging/flatpak/com.karmaa.termix.metainfo.xml flatpak-submission/
|
||||
cp packaging/flatpak/flathub.json flatpak-submission/
|
||||
|
||||
cp public/icon.svg flatpak-submission/com.karmaa.termix.svg
|
||||
convert public/icon.png -resize 256x256 flatpak-submission/icon-256.png
|
||||
@@ -768,23 +782,76 @@ jobs:
|
||||
sed -i "s/DATE_PLACEHOLDER/$RELEASE_DATE/g" flatpak-submission/com.karmaa.termix.metainfo.xml
|
||||
|
||||
- name: Upload Flatpak submission as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: flatpak-submission
|
||||
path: flatpak-submission/*
|
||||
retention-days: 30
|
||||
|
||||
- name: Check for Flathub token
|
||||
id: check_flathub_token
|
||||
run: |
|
||||
if [ -n "${{ secrets.FLATHUB_TOKEN }}" ]; then
|
||||
echo "has_token=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Open PR on Flathub repo
|
||||
if: steps.check_flathub_token.outputs.has_token == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FLATHUB_TOKEN }}
|
||||
VERSION: ${{ steps.package-version.outputs.version }}
|
||||
run: |
|
||||
FLATHUB_REPO="flathub/com.karmaa.termix"
|
||||
BRANCH="release-$VERSION"
|
||||
|
||||
git config --global user.name "LukeGus"
|
||||
git config --global user.email "bugattiguy527@gmail.com"
|
||||
|
||||
git clone "https://x-access-token:${GH_TOKEN}@github.com/${FLATHUB_REPO}.git" flathub-repo
|
||||
cd flathub-repo
|
||||
|
||||
git checkout -b "$BRANCH"
|
||||
|
||||
cp ../flatpak-submission/com.karmaa.termix.yml ./
|
||||
cp ../flatpak-submission/com.karmaa.termix.desktop ./
|
||||
cp ../flatpak-submission/com.karmaa.termix.metainfo.xml ./
|
||||
cp ../flatpak-submission/flathub.json ./
|
||||
cp ../flatpak-submission/com.karmaa.termix.svg ./
|
||||
cp ../flatpak-submission/icon-256.png ./
|
||||
cp ../flatpak-submission/icon-128.png ./
|
||||
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "No changes to submit for $VERSION; Flathub repo already up to date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "Update to $VERSION"
|
||||
git push origin "$BRANCH"
|
||||
|
||||
EXISTING_PR=$(gh pr list --repo "$FLATHUB_REPO" --head "$BRANCH" --state open --json number -q '.[0].number' || true)
|
||||
if [ -z "$EXISTING_PR" ]; then
|
||||
gh pr create --repo "$FLATHUB_REPO" \
|
||||
--base master \
|
||||
--head "$BRANCH" \
|
||||
--title "Update to $VERSION" \
|
||||
--body "Automated release update to version $VERSION."
|
||||
else
|
||||
echo "PR #$EXISTING_PR already open for $BRANCH."
|
||||
fi
|
||||
|
||||
submit-to-homebrew:
|
||||
runs-on: macos-latest
|
||||
if: github.event.inputs.artifact_destination == 'submit' && (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'macos')
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
if: inputs.artifact_destination == 'submit' && (inputs.build_type == 'all' || inputs.build_type == 'macos')
|
||||
needs: []
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Get version from package.json
|
||||
@@ -837,31 +904,36 @@ jobs:
|
||||
ruby -c homebrew-submission/Casks/t/termix.rb
|
||||
|
||||
- name: Upload Homebrew submission as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: homebrew-submission
|
||||
path: homebrew-submission/*
|
||||
retention-days: 30
|
||||
|
||||
upload-to-release:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: github.event.inputs.artifact_destination == 'release'
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
if: inputs.artifact_destination == 'release'
|
||||
needs: [build-windows, build-linux, build-macos]
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Get latest release tag
|
||||
- name: Resolve release tag
|
||||
id: get_release
|
||||
run: |
|
||||
echo "RELEASE_TAG=$(gh release list --repo ${{ github.repository }} --limit 1 --json tagName -q '.[0].tagName')" >> $GITHUB_ENV
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
INPUT_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
if [ -n "$INPUT_TAG" ]; then
|
||||
echo "RELEASE_TAG=$INPUT_TAG" >> $GITHUB_ENV
|
||||
else
|
||||
echo "RELEASE_TAG=$(gh release list --repo ${{ github.repository }} --limit 1 --json tagName -q '.[0].tagName')" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Upload artifacts to latest release
|
||||
run: |
|
||||
@@ -879,38 +951,29 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
submit-to-testflight:
|
||||
runs-on: macos-latest
|
||||
if: github.event.inputs.artifact_destination == 'submit' && (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'macos')
|
||||
submit-to-app-store:
|
||||
runs-on: blacksmith-6vcpu-macos-latest
|
||||
if: inputs.artifact_destination == 'submit' && (inputs.build_type == 'all' || inputs.build_type == 'macos')
|
||||
needs: []
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ inputs.source_ref || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
for i in 1 2 3;
|
||||
do
|
||||
if npm ci; then
|
||||
break
|
||||
else
|
||||
if [ $i -eq 3 ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
npm ci
|
||||
npm install --force @rollup/rollup-darwin-arm64
|
||||
npm install dmg-license
|
||||
|
||||
@@ -953,18 +1016,6 @@ jobs:
|
||||
|
||||
security find-identity -v -p codesigning $KEYCHAIN_PATH
|
||||
|
||||
- name: Build macOS App Store Package
|
||||
if: steps.check_certs.outputs.has_certs == 'true'
|
||||
env:
|
||||
ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES: true
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
run: |
|
||||
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||
BUILD_VERSION="${{ github.run_number }}"
|
||||
|
||||
npm run build && npx electron-builder --mac mas --universal --config.buildVersion="$BUILD_VERSION"
|
||||
|
||||
- name: Check for App Store Connect API credentials
|
||||
id: check_asc_creds
|
||||
run: |
|
||||
@@ -973,36 +1024,125 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Setup Ruby for Fastlane
|
||||
if: steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: "3.2"
|
||||
ruby-version: "3.3"
|
||||
bundler-cache: false
|
||||
|
||||
- name: Install Fastlane
|
||||
if: steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
run: |
|
||||
gem install fastlane -N
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
run: gem install fastlane -N
|
||||
|
||||
- name: Deploy to App Store Connect (TestFlight)
|
||||
if: steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
- name: Write App Store Connect API key
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
env:
|
||||
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
|
||||
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
|
||||
APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
|
||||
run: |
|
||||
# Write API key JSON that Fastlane expects; the PEM's newlines
|
||||
# must be preserved as literal \n escapes, not stripped, or
|
||||
# spaceship fails to parse the key (invalid curve name).
|
||||
mkdir -p /tmp/asc_keys
|
||||
KEY_P8_PATH="/tmp/asc_keys/AuthKey_${APPLE_KEY_ID}.p8"
|
||||
API_KEY_JSON="/tmp/asc_keys/api_key.json"
|
||||
|
||||
echo "$APPLE_KEY_CONTENT" | base64 --decode > "$KEY_P8_PATH"
|
||||
|
||||
KEY_ID="$APPLE_KEY_ID" ISSUER_ID="$APPLE_ISSUER_ID" KEY_P8_PATH="$KEY_P8_PATH" \
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const key = fs.readFileSync(process.env.KEY_P8_PATH, "utf8");
|
||||
process.stdout.write(JSON.stringify({
|
||||
key_id: process.env.KEY_ID,
|
||||
issuer_id: process.env.ISSUER_ID,
|
||||
key,
|
||||
in_house: false,
|
||||
}, null, 2) + "\n");
|
||||
' > "$API_KEY_JSON"
|
||||
|
||||
- name: Resolve next build number from App Store Connect
|
||||
id: build_number
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
run: |
|
||||
APP_VERSION=$(node -p "require('./package.json').version")
|
||||
OUT_FILE="$RUNNER_TEMP/latest_build_number.txt"
|
||||
LANE_DIR="$RUNNER_TEMP/asc_lane/fastlane"
|
||||
mkdir -p "$LANE_DIR"
|
||||
|
||||
cat > "$LANE_DIR/Fastfile" <<EOF
|
||||
default_platform(:mac)
|
||||
|
||||
lane :fetch_build_number do
|
||||
live_number = app_store_build_number(
|
||||
live: true,
|
||||
platform: "osx",
|
||||
api_key_path: "/tmp/asc_keys/api_key.json",
|
||||
app_identifier: "com.karmaa.termix",
|
||||
initial_build_number: 0,
|
||||
)
|
||||
pending_number = app_store_build_number(
|
||||
live: false,
|
||||
platform: "osx",
|
||||
api_key_path: "/tmp/asc_keys/api_key.json",
|
||||
app_identifier: "com.karmaa.termix",
|
||||
initial_build_number: 0,
|
||||
)
|
||||
number = [live_number, pending_number].max
|
||||
File.write("$OUT_FILE", number.to_s)
|
||||
end
|
||||
EOF
|
||||
|
||||
(cd "$RUNNER_TEMP/asc_lane" && fastlane fetch_build_number) 2>&1 || true
|
||||
|
||||
LATEST=""
|
||||
if [ -f "$OUT_FILE" ]; then
|
||||
LATEST=$(cat "$OUT_FILE" | tr -d '[:space:]')
|
||||
fi
|
||||
|
||||
if ! [[ "$LATEST" =~ ^[0-9]+$ ]]; then
|
||||
echo "Could not resolve latest build number from App Store Connect; falling back to run number."
|
||||
LATEST="${{ github.run_number }}"
|
||||
fi
|
||||
echo "build_version=$((LATEST + 1))" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build macOS App Store Package
|
||||
if: steps.check_certs.outputs.has_certs == 'true'
|
||||
env:
|
||||
ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES: true
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
run: |
|
||||
BUILD_VERSION="${{ steps.build_number.outputs.build_version || github.run_number }}"
|
||||
npm run build && npx electron-builder --mac mas --universal --config.buildVersion="$BUILD_VERSION"
|
||||
|
||||
- name: Upload and submit to Mac App Store
|
||||
if: steps.check_certs.outputs.has_certs == 'true' && steps.check_asc_creds.outputs.has_credentials == 'true'
|
||||
run: |
|
||||
PKG_FILE=$(find release -name "termix_macos_universal_mas.pkg" -type f | head -n 1)
|
||||
if [ -z "$PKG_FILE" ]; then
|
||||
echo "PKG file not found, exiting."
|
||||
echo "PKG file not found in release/; aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ~/private_keys
|
||||
echo "${{ secrets.APPLE_KEY_CONTENT }}" | base64 --decode > ~/private_keys/AuthKey_${{ secrets.APPLE_KEY_ID }}.p8
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
API_KEY_JSON="/tmp/asc_keys/api_key.json"
|
||||
|
||||
xcrun altool --upload-app -f "$PKG_FILE" \
|
||||
--type macos \
|
||||
--apiKey "${{ secrets.APPLE_KEY_ID }}" \
|
||||
--apiIssuer "${{ secrets.APPLE_ISSUER_ID }}"
|
||||
continue-on-error: true
|
||||
fastlane deliver \
|
||||
--pkg "$PKG_FILE" \
|
||||
--api_key_path "$API_KEY_JSON" \
|
||||
--app_version "$VERSION" \
|
||||
--skip_metadata true \
|
||||
--skip_screenshots true \
|
||||
--submit_for_review true \
|
||||
--automatic_release true \
|
||||
--precheck_include_in_app_purchases false \
|
||||
--submission_information "{\"export_compliance_uses_encryption\": false}" \
|
||||
--force true
|
||||
|
||||
- name: Clean up keychains
|
||||
if: always()
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
|
||||
rm -rf /tmp/asc_keys
|
||||
|
||||
@@ -10,10 +10,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
run: npm run generate:openapi
|
||||
|
||||
- name: Upload OpenAPI artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: openapi-spec
|
||||
path: openapi.json
|
||||
|
||||
@@ -13,18 +13,21 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run ESLint
|
||||
run: npx eslint .
|
||||
- name: Lint
|
||||
# npm run lint, not npx eslint — the script also checks that the
|
||||
# generated dialect schemas match schema.ts, which eslint cannot see.
|
||||
run: npm run lint
|
||||
|
||||
- name: Run Prettier check
|
||||
run: npx prettier --check .
|
||||
@@ -34,3 +37,76 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
database-dialects:
|
||||
name: Postgres and MySQL
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
|
||||
# The test suite only ever sees SQLite. Everything that differs per engine —
|
||||
# the RETURNING replacements, the read-then-write transactions, the
|
||||
# migrations themselves — is only covered here, against real servers.
|
||||
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
|
||||
|
||||
# Each run applies the migrations to an empty database first, so a
|
||||
# migration that does not apply cleanly fails the build.
|
||||
- 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
|
||||
|
||||
# The same repository suite the SQLite run executes, pointed at each
|
||||
# engine. This is where a dialect difference in a query shows up as a
|
||||
# failing assertion rather than as a bug report.
|
||||
- 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
|
||||
|
||||
@@ -0,0 +1,557 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mode:
|
||||
description: "Release mode"
|
||||
required: true
|
||||
default: Everything
|
||||
type: choice
|
||||
options:
|
||||
- Everything
|
||||
- Overwrite release
|
||||
- Dry run
|
||||
- Skip submit
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
prep:
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
outputs:
|
||||
version: ${{ steps.info.outputs.version }}
|
||||
release_tag: ${{ steps.info.outputs.release_tag }}
|
||||
mobile_version: ${{ steps.info.outputs.mobile_version }}
|
||||
dev_branch: ${{ steps.info.outputs.dev_branch }}
|
||||
steps:
|
||||
- name: Guard branch (release modes)
|
||||
if: ${{ inputs.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: ${{ inputs.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
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if [ "${{ inputs.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=release-$VERSION-tag" >> "$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$//')
|
||||
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"
|
||||
|
||||
- name: Validate release notes
|
||||
run: |
|
||||
node scripts/generate-release-body.cjs \
|
||||
--version "${{ steps.info.outputs.version }}" \
|
||||
--mobile-version "${{ steps.info.outputs.mobile_version }}" \
|
||||
--notes RELEASE_NOTES.md > /dev/null
|
||||
|
||||
normalize:
|
||||
needs: [prep]
|
||||
if: ${{ inputs.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.GHCR_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Lint and format
|
||||
run: |
|
||||
npm run lint:fix || true
|
||||
npm run format
|
||||
|
||||
- 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 [ "${{ inputs.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 }}"
|
||||
|
||||
crowdin:
|
||||
needs: [prep, normalize]
|
||||
if: ${{ inputs.mode != 'Dry run' && inputs.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.GHCR_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Merge main into dev branch
|
||||
run: |
|
||||
git config user.name "LukeGus"
|
||||
git config user.email "bugattiguy527@gmail.com"
|
||||
git fetch origin main
|
||||
git merge origin/main -X ours --no-edit || true
|
||||
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
|
||||
|
||||
- name: Clean up stale Crowdin git integration branch
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
PR_NUMBER=$(gh pr list --repo ${{ github.repository }} --head i18n_translate --state open --json number -q '.[0].number' || true)
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
gh pr close "$PR_NUMBER" --repo ${{ github.repository }} --delete-branch || true
|
||||
fi
|
||||
git push origin --delete i18n_translate || true
|
||||
|
||||
- name: Upload sources to Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: false
|
||||
create_pull_request: false
|
||||
push_translations: false
|
||||
token: ${{ secrets.CROWDIN_API_KEY }}
|
||||
project_id: "858252"
|
||||
env:
|
||||
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }}
|
||||
|
||||
- name: Machine pre-translate untranslated strings
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: node scripts/crowdin-pretranslate.cjs
|
||||
|
||||
- name: Download translations from Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
create_pull_request: false
|
||||
push_translations: false
|
||||
token: ${{ secrets.CROWDIN_API_KEY }}
|
||||
project_id: "858252"
|
||||
env:
|
||||
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_KEY }}
|
||||
|
||||
- name: Commit translations to dev branch
|
||||
run: |
|
||||
git config user.name "LukeGus"
|
||||
git config user.email "bugattiguy527@gmail.com"
|
||||
|
||||
git add src/ui/locales/translated
|
||||
if git diff --cached --quiet; then
|
||||
echo "No translation changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "chore: sync Crowdin translations for ${{ needs.prep.outputs.version }}"
|
||||
git push origin HEAD:"${{ needs.prep.outputs.dev_branch }}"
|
||||
|
||||
merge-to-main:
|
||||
needs: [prep, normalize, crowdin]
|
||||
if: ${{ always() && needs.prep.result == 'success' && (needs.normalize.result == 'success' || needs.normalize.result == 'skipped') && (needs.crowdin.result == 'success' || needs.crowdin.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 }}" \
|
||||
--notes RELEASE_NOTES.md > PR_BODY.md
|
||||
|
||||
- name: Open and squash-merge PR to main
|
||||
id: merge
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
DEV_BRANCH="${{ needs.prep.outputs.dev_branch }}"
|
||||
TITLE="release-${{ needs.prep.outputs.version }}"
|
||||
|
||||
if [ "${{ inputs.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 [ "${{ inputs.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, merge-to-main]
|
||||
uses: ./.github/workflows/docker.yml
|
||||
with:
|
||||
version: ${{ needs.prep.outputs.version }}
|
||||
build_type: Production
|
||||
dry_run: ${{ inputs.mode == 'Dry run' }}
|
||||
source_ref: ${{ needs.merge-to-main.outputs.build_ref }}
|
||||
secrets: inherit
|
||||
|
||||
create-release:
|
||||
needs: [prep, merge-to-main, docker]
|
||||
if: ${{ inputs.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: ${{ inputs.mode == 'Overwrite release' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
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: ${{ inputs.mode != 'Overwrite release' }}
|
||||
run: |
|
||||
node scripts/generate-release-body.cjs \
|
||||
--version "${{ needs.prep.outputs.version }}" \
|
||||
--mobile-version "${{ needs.prep.outputs.mobile_version }}" \
|
||||
--notes RELEASE_NOTES.md > RELEASE_BODY.md
|
||||
|
||||
- name: Create or update GitHub release
|
||||
if: ${{ inputs.mode != 'Overwrite release' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
TAG="${{ needs.prep.outputs.release_tag }}"
|
||||
TITLE="release-${{ 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
|
||||
|
||||
electron-release:
|
||||
needs: [prep, merge-to-main, create-release]
|
||||
if: ${{ always() && needs.merge-to-main.result == 'success' && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped') }}
|
||||
uses: ./.github/workflows/electron.yml
|
||||
with:
|
||||
build_type: all
|
||||
artifact_destination: ${{ inputs.mode == 'Dry run' && 'file' || 'release' }}
|
||||
release_tag: ${{ needs.prep.outputs.release_tag }}
|
||||
source_ref: ${{ needs.merge-to-main.outputs.build_ref }}
|
||||
secrets: inherit
|
||||
|
||||
electron-submit:
|
||||
needs: [prep, merge-to-main, electron-release]
|
||||
if: ${{ inputs.mode != 'Dry run' && inputs.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: ${{ inputs.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.GHCR_TOKEN }}
|
||||
|
||||
- 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
|
||||
|
||||
docs:
|
||||
needs: [prep, merge-to-main]
|
||||
if: ${{ always() && needs.merge-to-main.result == 'success' && inputs.mode != 'Overwrite release' }}
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout Termix
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ needs.merge-to-main.outputs.build_ref }}
|
||||
fetch-depth: 1
|
||||
path: termix
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: "termix/.nvmrc"
|
||||
cache: "npm"
|
||||
cache-dependency-path: termix/package-lock.json
|
||||
|
||||
- name: Generate OpenAPI spec
|
||||
working-directory: termix
|
||||
run: |
|
||||
npm ci
|
||||
npm run generate:openapi
|
||||
|
||||
- name: Checkout Docs repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: Termix-SSH/Docs
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GHCR_TOKEN }}
|
||||
path: docs-repo
|
||||
|
||||
- name: Create docs release branch
|
||||
working-directory: docs-repo
|
||||
run: git checkout -B "dev-${{ needs.prep.outputs.version }}"
|
||||
|
||||
- name: Overwrite OpenAPI spec and regenerate API docs
|
||||
working-directory: docs-repo
|
||||
run: |
|
||||
cp ../termix/openapi.json static/openapi.json
|
||||
npm ci
|
||||
npm run docusaurus gen-api-docs termix
|
||||
|
||||
- name: Commit and push docs branch
|
||||
working-directory: docs-repo
|
||||
run: |
|
||||
git config user.name "LukeGus"
|
||||
git config user.email "bugattiguy527@gmail.com"
|
||||
|
||||
if git diff --quiet; then
|
||||
echo "No docs changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${{ inputs.mode }}" = "Dry run" ]; then
|
||||
echo "DRY RUN: would commit and push the following docs changes to dev-${{ needs.prep.outputs.version }}:"
|
||||
git --no-pager diff --stat
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "feat: update API docs for ${{ needs.prep.outputs.version }}"
|
||||
git push --force origin "dev-${{ needs.prep.outputs.version }}"
|
||||
|
||||
- name: Open and squash-merge docs PR
|
||||
if: ${{ inputs.mode != 'Dry run' }}
|
||||
working-directory: docs-repo
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
BRANCH="dev-${{ needs.prep.outputs.version }}"
|
||||
if ! git ls-remote --exit-code origin "refs/heads/$BRANCH" >/dev/null 2>&1; then
|
||||
echo "No docs branch pushed (nothing changed); skipping PR."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PR_NUMBER=$(gh pr list --repo Termix-SSH/Docs --head "$BRANCH" --base main --state open --json number -q '.[0].number' || true)
|
||||
if [ -z "$PR_NUMBER" ]; then
|
||||
PR_URL=$(gh pr create --repo Termix-SSH/Docs \
|
||||
--base main --head "$BRANCH" \
|
||||
--title "release-${{ needs.prep.outputs.version }}" \
|
||||
--body "API docs for ${{ needs.prep.outputs.version }}")
|
||||
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
|
||||
fi
|
||||
|
||||
if [ -z "$PR_NUMBER" ]; then
|
||||
echo "Failed to find or create a PR for $BRANCH."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gh pr merge "$PR_NUMBER" --repo Termix-SSH/Docs --squash --admin
|
||||
|
||||
publish-youtube:
|
||||
needs: [prep, electron-release]
|
||||
if: ${{ always() && inputs.mode != 'Dry run' && inputs.mode != 'Overwrite release' && needs.electron-release.result == 'success' }}
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
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: Set release video to public
|
||||
env:
|
||||
YOUTUBE_CLIENT_ID: ${{ secrets.YOUTUBE_CLIENT_ID }}
|
||||
YOUTUBE_CLIENT_SECRET: ${{ secrets.YOUTUBE_CLIENT_SECRET }}
|
||||
YOUTUBE_REFRESH_TOKEN: ${{ secrets.YOUTUBE_REFRESH_TOKEN }}
|
||||
run: node scripts/publish-youtube.cjs
|
||||
|
||||
cleanup:
|
||||
needs:
|
||||
[
|
||||
prep,
|
||||
merge-to-main,
|
||||
electron-release,
|
||||
cask-commit-back,
|
||||
docs,
|
||||
publish-youtube,
|
||||
]
|
||||
if: ${{ always() && (inputs.mode == 'Everything' || inputs.mode == 'Skip submit') && needs.merge-to-main.result == 'success' && needs.electron-release.result == 'success' }}
|
||||
runs-on: blacksmith-2vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Delete dev branch in Termix
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
git push https://x-access-token:${{ secrets.GHCR_TOKEN }}@github.com/${{ github.repository }}.git \
|
||||
--delete "${{ needs.prep.outputs.dev_branch }}" || true
|
||||
|
||||
- name: Delete dev branch in Docs
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
run: |
|
||||
git push https://x-access-token:${{ secrets.GHCR_TOKEN }}@github.com/Termix-SSH/Docs.git \
|
||||
--delete "dev-${{ needs.prep.outputs.version }}" || true
|
||||
@@ -7,8 +7,10 @@ pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
src/mcp-server/node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
.vscode/
|
||||
@@ -30,3 +32,6 @@ dist-ssr
|
||||
electron/build-info.cjs
|
||||
/.mcp.json
|
||||
/CLAUDE.md
|
||||
/old_db/
|
||||
/scripts/fix-bugs.mjs
|
||||
/scripts/fix-features.mjs
|
||||
|
||||
@@ -5,6 +5,7 @@ dist-ssr
|
||||
release
|
||||
|
||||
node_modules
|
||||
src/mcp-server/node_modules
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
@@ -16,3 +17,6 @@ db
|
||||
*.min.js
|
||||
*.min.css
|
||||
openapi.json
|
||||
|
||||
# Generated by drizzle-kit; formatting is the tool's own
|
||||
drizzle/
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
mail@termix.site.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,6 +1,6 @@
|
||||
cask "termix" do
|
||||
version "2.2.0"
|
||||
sha256 "62030994ecd8de264f7edae965caf57fabb9f9014007063a35db8cab4c6e818c"
|
||||
version "2.6.0"
|
||||
sha256 "1ea70f6d909ac844cae40e834d54f12151b20f4bd0da15cd624b8cf9ecc2555a"
|
||||
|
||||
url "https://github.com/Termix-SSH/Termix/releases/download/release-#{version}-tag/termix_macos_universal_dmg.dmg"
|
||||
name "Termix"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"drips": {
|
||||
"ethereum": {
|
||||
"ownedBy": "0x67e0C779119D9BcC2187564A66B80a58767d05d1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,274 @@
|
||||
# Repo Stats
|
||||
<div align="center">
|
||||
|
||||
<p align="center">
|
||||
🇺🇸 English · <a href="readme/README-CN.md">🇨🇳 中文</a> · <a href="readme/README-JA.md">🇯🇵 日本語</a> · <a href="readme/README-KO.md">🇰🇷 한국어</a> · <a href="readme/README-FR.md">🇫🇷 Français</a> · <a href="readme/README-DE.md">🇩🇪 Deutsch</a> · <a href="readme/README-ES.md">🇪🇸 Español</a> · <a href="readme/README-PT.md">🇧🇷 Português</a> · <a href="readme/README-RU.md">🇷🇺 Русский</a> · <a href="readme/README-AR.md">🇸🇦 العربية</a> · <a href="readme/README-HI.md">🇮🇳 हिन्दी</a> · <a href="readme/README-TR.md">🇹🇷 Türkçe</a> · <a href="readme/README-VI.md">🇻🇳 Tiếng Việt</a> · <a href="readme/README-IT.md">🇮🇹 Italiano</a>
|
||||
<img src="./public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Self-hosted SSH management and remote desktop access</p>
|
||||
|
||||
<p>
|
||||
English ·
|
||||
<a href="docs/readme/README-CN.md">中文</a> ·
|
||||
<a href="docs/readme/README-JA.md">日本語</a> ·
|
||||
<a href="docs/readme/README-KO.md">한국어</a> ·
|
||||
<a href="docs/readme/README-FR.md">Français</a> ·
|
||||
<a href="docs/readme/README-DE.md">Deutsch</a> ·
|
||||
<a href="docs/readme/README-ES.md">Español</a> ·
|
||||
<a href="docs/readme/README-PT.md">Português</a> ·
|
||||
<a href="docs/readme/README-RU.md">Русский</a> ·
|
||||
<a href="docs/readme/README-AR.md">العربية</a> ·
|
||||
<a href="docs/readme/README-HI.md">हिन्दी</a> ·
|
||||
<a href="docs/readme/README-TR.md">Türkçe</a> ·
|
||||
<a href="docs/readme/README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="docs/readme/README-IT.md">Italiano</a>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||

|
||||
<a href="https://discord.gg/jVQGdvHDrf"><img alt="Discord" src="https://img.shields.io/discord/1347374268253470720"></a>
|
||||
<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>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/RepoOfTheDay.png" alt="Repo of the Day Achievement" style="width: 300px; height: auto;">
|
||||
<br>
|
||||
<small style="color: #666;">Achieved on September 1st, 2025</small>
|
||||
<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>
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://github.com/Termix-SSH/Termix">
|
||||
<img alt="Termix Banner" src=./repo-images/HeaderImage.png style="width: auto; height: auto;"> </a>
|
||||
|
||||
Termix is free and open source. If you find it useful, consider [donating](https://donate.termix.site/) to help cover server costs and development time.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="./docs/repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="docs/repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Achieved on September 1st, 2025</sub>
|
||||
</p>
|
||||
|
||||
# Overview
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/Termix-SSH/Termix">
|
||||
<img alt="Termix Banner" src=./public/icon.svg style="width: 250px; height: 250px;"> </a>
|
||||
</p>
|
||||
<br />
|
||||
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a multi-platform
|
||||
solution for managing your servers and infrastructure through a single, intuitive interface. Termix offers SSH terminal
|
||||
access, remote desktop control (RDP, VNC, Telnet), SSH tunneling capabilities, remote SSH file management, and many other tools. Termix is the perfect
|
||||
free and self-hosted alternative to Termius available for all platforms.
|
||||
## Overview
|
||||
|
||||
# Features
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a multi-platform solution for managing your servers and infrastructure through a single, intuitive interface. Termix offers SSH terminal access, remote desktop control (RDP, VNC, Telnet), SSH tunneling capabilities, remote file management, and many other tools. Termix is the perfect free and self-hosted alternative to Termius available for all platforms.
|
||||
|
||||
<br />
|
||||
|
||||
## Features
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Terminal Access:**
|
||||
Full-featured terminal with split-screen support (up to 4 panels) with a browser-like tab system. Includes support for customizing the terminal including common terminal themes, fonts, and other components.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Remote Desktop Access:**
|
||||
RDP, VNC, and Telnet support over the browser with complete customization and split screening.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Tunnel Management:**
|
||||
Create and manage server-to-server SSH tunnels with automatic reconnection, health monitoring, and local, remote, or dynamic SOCKS forwarding. Desktop client-to-server tunnel settings are stored locally per desktop install, optional C2S preset snapshots can be saved to the server, renamed, loaded, or deleted when you want to move a local tunnel configuration between clients.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Remote File Manager:**
|
||||
Manage files directly on remote servers with support for viewing and editing code, images, audio, and video. Upload, download, rename, delete, and move files seamlessly with sudo support. Includes support for moving files from server to server.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker and Podman Management:**
|
||||
Start, stop, pause, remove containers. View container stats. Control containers using a docker exec terminal. Supports both Docker and Podman as the container runtime. It was not made to replace Portainer or Dockge but rather to simply manage your containers compared to creating them.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Host Manager:**
|
||||
Save, organize, and manage your SSH connections with tags and folders (folder customization and nested folder support), and easily save reusable login info while being able to automate the deployment of SSH keys.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Host Metrics:**
|
||||
View CPU, memory, disk usage, network, uptime, system information, firewall, port monitor, log viewer, users/permissions, certificates, and many more which work on most Linux based servers. Includes time-series history graphs and threshold-based alerts with ntfy and webhook support.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**User Authentication:**
|
||||
Secure user management with admin controls (can edit other users information) and OIDC/LDAP/SSO (with access control), 2FA (TOTP), and passkey (WebAuthn) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together. View audit log of all users actions.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale Integration:**
|
||||
List devices from your tailnet to quickly add them as hosts, and connect using Tailscale SSH as an authentication method, letting your tailnet ACLs handle authorization without storing credentials.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Sharing:**
|
||||
Create roles and share hosts across users/roles. Supports all auth types and all host protocols.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Serial Connections:**
|
||||
Connect to serial devices (routers, switches, microcontrollers, etc.) directly from the browser or desktop app. Configure baud rate, data bits, stop bits, and parity. Uses the Web Serial API in supported browsers or a native backend in the Electron app.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Alerts:**
|
||||
Set threshold-based alert rules on host metrics (CPU, memory, disk, etc.) and get notified via ntfy or webhooks when they fire. View firing and resolved alerts in a history log.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Homepage:**
|
||||
A fully customizable homepage with a drag-and-drop widget grid. Add widgets for host status, service links, clocks, notes, RSS feeds, weather, Docker containers, host metrics charts, embedded terminals, iframes, and more.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Database Encryption:**
|
||||
Backend stored as encrypted SQLite database files. View [docs](https://docs.termix.site/security) for more.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Network Graph:**
|
||||
Customize your Dashboard to visualize your homelab based off your SSH connections with status support.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Tools:**
|
||||
Create reusable command snippets that execute with a single click. Run one command simultaneously across multiple open terminals.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Persistent Tabs:**
|
||||
SSH sessions and tabs stay open across devices/refreshes if enabled in user profile.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Languages:**
|
||||
Built-in support ~30 languages (managed by [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Session Sharing:**
|
||||
Share a live terminal, RDP, VNC, or Telnet session with others in real time. Share via a link (joined anonymously, no account needed) or with a specific Termix user, and choose read-only or read-write access. Shares can expire automatically or be revoked at any time, and session sharing can be toggled globally or per-host.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Desktop Standalone + 2-Way Sync:**
|
||||
The Electron desktop app runs fully standalone with its own local backend and database, no server required. Optionally connect it to a remote Termix server for automatic two-way sync of hosts, credentials, snippets, and more, and choose whether SSH connections are started locally or through the remote server.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>More features</b></summary>
|
||||
<br />
|
||||
|
||||
- **SSH Terminal Access** - Full-featured terminal with split-screen support (up to 4 panels) with a browser-like tab system. Includes support for customizing the terminal including common terminal themes, fonts, and other components.
|
||||
- **Remote Desktop Access** - RDP, VNC, and Telnet support over the browser with complete customization and split screening
|
||||
- **SSH Tunnel Management** - Create and manage server-to-server SSH tunnels with automatic reconnection, health monitoring, and local, remote, or dynamic SOCKS forwarding. Desktop client-to-server tunnel settings are stored locally per desktop install, optional C2S preset snapshots can be saved to the server, renamed, loaded, or deleted when you want to move a local tunnel configuration between clients.
|
||||
- **Remote File Manager** - Manage files directly on remote servers with support for viewing and editing code, images, audio, and video. Upload, download, rename, delete, and move files seamlessly with sudo support.
|
||||
- **Docker Management** - Start, stop, pause, remove containers. View container stats. Control container using docker exec terminal. It was not made to replace Portainer or Dockge but rather to simply manage your containers compared to creating them.
|
||||
- **SSH Host Manager** - Save, organize, and manage your SSH connections with tags and folders, and easily save reusable login info while being able to automate the deployment of SSH keys
|
||||
- **Server Stats** - View CPU, memory, and disk usage along with network, uptime, system information, firewall, port monitor, on most Linux based servers
|
||||
- **Dashboard** - View server information at a glance on your dashboard
|
||||
- **RBAC** - Create roles and share hosts across users/roles
|
||||
- **User Authentication** - Secure user management with admin controls and OIDC (with access control) and 2FA (TOTP) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together.
|
||||
- **Database Encryption** - Backend stored as encrypted SQLite database files. View [docs](https://docs.termix.site/security) for more.
|
||||
- **API Keys** - Create user-scoped API keys with expiration dates to be used for automation/CI
|
||||
- **Data Export/Import** - Export and import SSH hosts, credentials, and file manager data
|
||||
- **Automatic SSL Setup** - Built-in SSL certificate generation and management with HTTPS redirects
|
||||
- **Modern UI** - Clean desktop/mobile-friendly interface built with React, Tailwind CSS, and Shadcn. Choose between many different UI themes including light, dark, Dracula, etc. Use URL routes to open any connection in full-screen.
|
||||
- **Languages** - Built-in support ~30 languages (managed by [Crowdin](https://docs.termix.site/translations))
|
||||
- **Platform Support** - Available as a web app, desktop application (Windows, Linux, and macOS, can be run standalone without Termix backend), PWA, and dedicated mobile/tablet app for iOS and Android.
|
||||
- **SSH Tools** - Create reusable command snippets that execute with a single click. Run one command simultaneously across multiple open terminals.
|
||||
- **Command History** - Auto-complete and view previously ran SSH commands
|
||||
- **Quick Connect** - Connect to a server without having to save the connection data
|
||||
- **Command Palette** - Double tap left shift to quickly access SSH connections with your keyboard
|
||||
- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, etc.
|
||||
- **Network Graph** - Customize your Dashboard to visualize your homelab based off your SSH connections with status support
|
||||
- **Persistent Tabs** - SSH sessions and tabs stay open across devices/refreshes if enabled in user profile
|
||||
- **Proxmox Integration** - Auto-add hosts into Termix from your Proxmox instance
|
||||
- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal logging, SSH agent forwarding, Bitwarden SSH agent, HashiCorp Vault SSH signing, and more.
|
||||
- **Termix ID** - A sshid.io equivalent built into Termix. Claim a handle, publish your public SSH keys at a resolver URL, and use a built-in CA to issue SSH certificates.
|
||||
|
||||
# Planned Features
|
||||
</details>
|
||||
|
||||
See [Projects](https://github.com/orgs/Termix-SSH/projects/2) for all planned features. If you are looking to contribute, see [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
<br />
|
||||
|
||||
# Installation
|
||||
## Platform Support
|
||||
|
||||
Supported Devices:
|
||||
<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 · MSI Installer · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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>
|
||||
|
||||
- Website (any modern browser on any platform like Chrome, Safari, and Firefox) (includes PWA support)
|
||||
- Windows (x64/ia32)
|
||||
- Portable
|
||||
- MSI Installer
|
||||
- Chocolatey Package Manager
|
||||
- Linux (x64/ia32)
|
||||
- Portable
|
||||
- AUR
|
||||
- AppImage
|
||||
- Deb
|
||||
- Flatpak
|
||||
- macOS (x64/ia32 on v12.0+)
|
||||
- Apple App Store
|
||||
- DMG
|
||||
- Homebrew
|
||||
- iOS/iPadOS (v15.1+)
|
||||
- Apple App Store
|
||||
- IPA
|
||||
- Android (v7.0+)
|
||||
- Google Play Store
|
||||
- APK
|
||||
<br />
|
||||
|
||||
Visit the Termix [Docs](https://docs.termix.site/install) for more information on how to install Termix on all platforms. Otherwise, view
|
||||
a sample Docker Compose file here (you can omit guacd and the network if you don't plan on using remote desktop features):
|
||||
## Installation
|
||||
|
||||
Visit the [Termix Docs](https://docs.termix.site/install) for full installation instructions across all platforms.
|
||||
|
||||
Sample Docker Compose file (you can omit `guacd` and the network if you don't plan on using remote desktop features):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -128,80 +305,139 @@ networks:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
# Sponsors
|
||||
### Cloud Hosting
|
||||
|
||||
<p align="left">
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="50" alt="DigitalOcean">
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="50" alt="Crowdin">
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="50" alt="Blacksmith">
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="50" alt="Crowdflare">
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="50" alt="TailScale">
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="50" alt="Akamai">
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="50" alt="AWS">
|
||||
</a>
|
||||
</p>
|
||||
You can also run the Termix server on a cloud VPS instead of inside your own network. If Termix runs on the network it manages, an outage takes Termix with it, and your hosts and saved sessions are stuck inside the system you are trying to fix. Hosting it externally keeps it reachable no matter what happens to your network, and gives you a static IP and access from anywhere without a VPN or port forward.
|
||||
|
||||
# Support
|
||||
[GINERNET](https://docs.termix.site/install/ginernet) is a sponsor of Termix, and there is a full step by step guide for deploying to their VPS platform in the docs.
|
||||
|
||||
If you need help or want to request a feature with Termix, visit the [Issues](https://github.com/Termix-SSH/Support/issues) page, log in, and press `New Issue`.
|
||||
Please be as detailed as possible in your issue, preferably written in English. You can also join the [Discord](https://discord.gg/jVQGdvHDrf) server and visit the support
|
||||
channel, however, response times may be longer.
|
||||
<br />
|
||||
|
||||
# Screenshots
|
||||
## Telemetry
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
Termix sends a small anonymous usage ping once every 24 hours to help understand how many instances are running and which features are actually used. This only includes a randomly generated instance ID, a count of users and hosts, the app version, and whether certain features (terminal, file manager, tunnels, docker, etc.) were used in the last 24 hours. It never includes usernames, hostnames, IP addresses, credentials, or any other identifying or connection data.
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 1.png" width="400" alt="Termix Demo 1"/>
|
||||
<img src="./repo-images/Image 2.png" width="400" alt="Termix Demo 2"/>
|
||||
</p>
|
||||
This is opt-out and enabled by default. You can disable it at any time in Admin Settings under General, or set `ENABLE_TELEMETRY=false` to turn it off before you ever spin-up Termix.
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 3.png" width="400" alt="Termix Demo 3"/>
|
||||
<img src="./repo-images/Image 4.png" width="400" alt="Termix Demo 4"/>
|
||||
</p>
|
||||
<br />
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 5.png" width="400" alt="Termix Demo 5"/>
|
||||
<img src="./repo-images/Image 6.png" width="400" alt="Termix Demo 6"/>
|
||||
</p>
|
||||
## Donate
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 7.png" width="400" alt="Termix Demo 7"/>
|
||||
<img src="./repo-images/Image 8.png" width="400" alt="Termix Demo 8"/>
|
||||
</p>
|
||||
Termix is free and open source with no subscriptions or paid plans. If you find it useful, consider donating to help cover server costs, domains, and development time. Donations also help fund the time to research and learn what's needed to build features like SAML, Kubernetes, and Agent support. Track progress and donate below.
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 9.png" width="400" alt="Termix Demo 9"/>
|
||||
<img src="./repo-images/Image 10.png" width="400" alt="Termix Demo 10"/>
|
||||
</p>
|
||||
[Donate](https://donate.termix.site/)
|
||||
|
||||
<p align="center">
|
||||
<img src="./repo-images/Image 11.png" width="400" alt="Termix Demo 11"/>
|
||||
<img src="./repo-images/Image 12.png" width="400" alt="Termix Demo 12"/>
|
||||
</p>
|
||||
<br />
|
||||
|
||||
Some videos and images may be out of date or may not perfectly showcase features.
|
||||
## Sponsors
|
||||
|
||||
# License
|
||||
Interested in a paid placement to support development? Email [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
Distributed under the Apache License Version 2.0. See LICENSE for more information.
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
<a href="https://ginernet.com/">
|
||||
<img src="https://ginernet.com/img/logo-web.png" height="40" alt="Ginernet" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Support
|
||||
|
||||
If you need help or want to request a feature with Termix, visit the [Issues](https://github.com/Termix-SSH/Support/issues) page, log in, and press `New Issue`. Please be as detailed as possible in your issue, preferably written in English. You can also join the [Discord](https://discord.gg/jVQGdvHDrf) server and visit the support channel, however, response times may be longer.
|
||||
|
||||
<br />
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Watch update overviews on YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./docs/repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="./docs/repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Some videos and images may be out of date or may not perfectly showcase features.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Planned Features
|
||||
|
||||
See [Projects](https://github.com/orgs/Termix-SSH/projects/5) for all planned features. If you are looking to contribute, see [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the Apache License Version 2.0. See `LICENSE` for more information.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<!-- SUMMARY -->
|
||||
|
||||
Standalone-first Electron desktop app with optional remote sync, shared/multiplayer terminal and remote desktop sessions, improved SSH MFA support, custom key shortcuts, bug fixes across terminal, RDP/VNC, mobile, and auth.
|
||||
|
||||
<!-- /SUMMARY -->
|
||||
|
||||
<!-- YOUTUBE -->
|
||||
|
||||
https://youtu.be/g0QjNdV3YYY
|
||||
|
||||
<!-- /YOUTUBE -->
|
||||
|
||||
<!-- UPDATE_LOG -->
|
||||
|
||||
- Added support for multi disk usage in file manager/host metrics
|
||||
- Added better Ctrl + F terminal search
|
||||
- Added right click menu on app rail to pin sidebar faster
|
||||
- Support for overriding shared SSH credential
|
||||
- Added mapping for OIDC provider groups to RBAC roles
|
||||
- Added host export dialog for more customizable host exporting
|
||||
- Initial groundwork for supporting more database types (postgres and mysql)
|
||||
- Added audit log export (CSV/NDJSON) and optional live forwarding to a SIEM
|
||||
- Added configurable audit log retention by age and row count
|
||||
- Audit entries for file manager, RDP/VNC/Telnet, Docker and tunnel sessions
|
||||
- Encrypted SSO secrets instead of BASE64 encoding them
|
||||
- Added support for Tailscale SSH check mode with in-terminal browser authentication
|
||||
|
||||
<!-- /UPDATE_LOG -->
|
||||
|
||||
<!-- BUG_FIXES -->
|
||||
|
||||
- Hardened nginx headers/asset caching
|
||||
- Deleting an account no longer deletes its audit entries and session recordings
|
||||
- Made logger display expanded error messages
|
||||
- Removed phantom port knocking
|
||||
- Fixed Proxmox guest discovery failures over jump host
|
||||
- Compare sync cursors independently of timestamp layout
|
||||
- Fixed sync deleting not reaching other side
|
||||
- Remote sync stalling after first pass and never propagating deletions
|
||||
- DB_FILE_ENCRYPTION variable loading DB file as empty
|
||||
- Removed unneeded field encryption boundaries
|
||||
- SSH login alerts being dropped silently
|
||||
- Honor lookupOptions.all in custom DNS lookup hook
|
||||
- Jump host SOCKS proxy settings being ignored
|
||||
- Jump host tunnels not reachable by guacd
|
||||
- Per-host RDP/VNC recording flags being ignored
|
||||
- RDP sessions not using the configured resolution
|
||||
- OIDC login failing with unverifiable ID tokens or JWKs without alg
|
||||
- Refuse to start with an empty database when data exists elsewhere
|
||||
- Database not persisting during container shutdown
|
||||
- Host command history setting not saving
|
||||
- Desktop preference sync and remote sync account identity
|
||||
- Desktop guacd calls not routed to the connected remote server
|
||||
- File manager navigation getting stuck after permission errors
|
||||
- Read-only shared hosts could be dragged into folders
|
||||
- Terminal highlighting breaking inside split control strings
|
||||
- Windows terminal Tab key and Android hardware keyboard keys
|
||||
- tmux monitor failing on Tailscale-authenticated hosts
|
||||
- Database export not staying same-origin on localhost
|
||||
- Snippet execution results not reported correctly
|
||||
- Shared hosts appearing twice
|
||||
- Wake-on-LAN broadcast address being dropped
|
||||
- Sharing an empty folder was rejected
|
||||
- Remote sync losing references between linked records
|
||||
- Desktop app failing to find its backend on some architectures
|
||||
- Centralized outbound address validation for homepage proxy requests
|
||||
- Default font size to medium instead of large
|
||||
- Tailscale hosts hanging on connect when the tailnet ACL requires a periodic check
|
||||
|
||||
<!-- /BUG_FIXES -->
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "dev-2.6.1"
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": [
|
||||
"**",
|
||||
"!!build",
|
||||
"!!coverage",
|
||||
"!!dist",
|
||||
"!!dist-ssr",
|
||||
"!!release",
|
||||
"!!node_modules",
|
||||
"!!src/mcp-server/node_modules",
|
||||
"!!db",
|
||||
"!!.env",
|
||||
"!!**/*.min.js",
|
||||
"!!**/*.min.css",
|
||||
"!!openapi.json"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80,
|
||||
"lineEnding": "lf"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": false
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double",
|
||||
"semicolons": "always",
|
||||
"trailingCommas": "all",
|
||||
"arrowParentheses": "always"
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"formatter": {
|
||||
"trailingCommas": "none"
|
||||
}
|
||||
},
|
||||
"css": {
|
||||
"parser": {
|
||||
"tailwindDirectives": true
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"style": "radix-lyra",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/index.css",
|
||||
"baseColor": "zinc",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"rtl": false,
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
@@ -17,5 +19,7 @@
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
"menuColor": "default",
|
||||
"menuAccent": "subtle",
|
||||
"registries": {}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
project_id: "858252"
|
||||
api_token: "env:CROWDIN_API_TOKEN"
|
||||
|
||||
files:
|
||||
- source: /src/locales/en.json
|
||||
translation: /src/locales/translated/%locale_with_underscore%.json
|
||||
- source: /src/ui/locales/en.json
|
||||
translation: /src/ui/locales/translated/%locale_with_underscore%.json
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Stage 1: Install dependencies
|
||||
FROM node:24-slim AS deps
|
||||
FROM node:26-slim AS deps
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
||||
@@ -8,7 +8,10 @@ COPY package*.json ./
|
||||
COPY .npmrc ./
|
||||
COPY vendor ./vendor
|
||||
|
||||
COPY scripts/patch-guacamole-lite.cjs ./scripts/
|
||||
|
||||
RUN npm ci --ignore-scripts && \
|
||||
node scripts/patch-guacamole-lite.cjs && \
|
||||
npm cache clean --force
|
||||
|
||||
# Stage 2: Build frontend
|
||||
@@ -33,7 +36,7 @@ RUN npm rebuild better-sqlite3
|
||||
RUN npm run build:backend
|
||||
|
||||
# Stage 4: Production dependencies only
|
||||
FROM node:24-slim AS production-deps
|
||||
FROM node:26-slim AS production-deps
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
||||
@@ -42,19 +45,23 @@ COPY package*.json ./
|
||||
COPY .npmrc ./
|
||||
COPY vendor ./vendor
|
||||
|
||||
COPY scripts/patch-guacamole-lite.cjs ./scripts/
|
||||
|
||||
RUN npm ci --omit=dev --ignore-scripts && \
|
||||
npm rebuild better-sqlite3 bcryptjs && \
|
||||
node scripts/patch-guacamole-lite.cjs && \
|
||||
npm rebuild better-sqlite3 bcryptjs ssh2 && \
|
||||
npm cache clean --force
|
||||
|
||||
# Stage 5: Final optimized image
|
||||
FROM node:24-slim
|
||||
FROM node:26-slim
|
||||
WORKDIR /app
|
||||
|
||||
ENV DATA_DIR=/app/data \
|
||||
PORT=8080 \
|
||||
NODE_ENV=production
|
||||
NODE_ENV=production \
|
||||
POSTHOG_API_KEY=phc_xM8UznirsFxUkGE68gH4jzeqevf4kh76wGw7Ci7hH2dd
|
||||
|
||||
RUN apt-get update && apt-get install -y nginx gettext-base openssl ca-certificates gosu wget && \
|
||||
RUN apt-get update && apt-get install -y nginx gettext-base openssl ca-certificates gosu wget certbot python3-certbot-dns-cloudflare && \
|
||||
update-ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
mkdir -p /app/data /app/uploads /app/data/.opk /app/nginx /tmp/nginx && \
|
||||
@@ -65,16 +72,17 @@ 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=frontend-builder /app/src/locales /app/html/locales
|
||||
COPY --chown=node:node --from=frontend-builder /app/public/fonts /app/html/fonts
|
||||
|
||||
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
|
||||
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} 30001 30002 30003 30004 30005 30006
|
||||
EXPOSE ${PORT} 30001 30002 30003 30004 30005 30006 30007 30008 30009 30010 30011 30012
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD wget -q -O /dev/null http://localhost:30001/health || exit 1
|
||||
|
||||
@@ -12,6 +12,9 @@ services:
|
||||
environment:
|
||||
PORT: "8080"
|
||||
NODE_ENV: development
|
||||
GUACD_HOST: "guacd-dev"
|
||||
GUACD_TUNNEL_HOST: "termix-dev"
|
||||
GUACD_RECORDING_PATH: "/termix-data/session_recordings/guacamole"
|
||||
depends_on:
|
||||
- guacd-dev
|
||||
networks:
|
||||
@@ -21,6 +24,8 @@ services:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd-dev
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- termix-dev-data:/termix-data
|
||||
networks:
|
||||
- termix-dev-net
|
||||
|
||||
@@ -9,6 +9,9 @@ services:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
GUACD_HOST: "guacd"
|
||||
GUACD_TUNNEL_HOST: "termix"
|
||||
GUACD_RECORDING_PATH: "/termix-data/session_recordings/guacamole"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
@@ -18,8 +21,8 @@ services:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
volumes:
|
||||
- termix-data:/termix-data
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ if [ "$(id -u)" = "0" ]; then
|
||||
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 /tmp/nginx 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"
|
||||
|
||||
@@ -41,7 +41,7 @@ fi
|
||||
mkdir -p /tmp/nginx
|
||||
envsubst '${PORT} ${SSL_PORT} ${SSL_CERT_PATH} ${SSL_KEY_PATH}' < $NGINX_CONF_SOURCE > /tmp/nginx/nginx.conf
|
||||
|
||||
mkdir -p /app/data /app/uploads /app/data/.opk
|
||||
mkdir -p /app/data /app/uploads /app/data/.opk /app/data/acme-webroot/.well-known/acme-challenge
|
||||
chmod 755 /app/data /app/uploads /app/data/.opk 2>/dev/null || true
|
||||
|
||||
if [ -w /app/data ]; then
|
||||
@@ -135,6 +135,19 @@ fi
|
||||
echo "Starting nginx..."
|
||||
nginx -c /tmp/nginx/nginx.conf
|
||||
|
||||
# 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%/}"
|
||||
find /app/html -name "index.html" -exec sed -i "s|window.__TERMIX_BASE_PATH__ = \"\"|window.__TERMIX_BASE_PATH__ = \"$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..."
|
||||
cd /app
|
||||
export NODE_ENV=production
|
||||
@@ -150,8 +163,4 @@ else
|
||||
echo "Warning: package.json not found"
|
||||
fi
|
||||
|
||||
node dist/backend/backend/starter.js
|
||||
|
||||
echo "All services started"
|
||||
|
||||
tail -f /dev/null
|
||||
exec node dist/backend/backend/starter.js
|
||||
|
||||
@@ -11,6 +11,8 @@ 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;
|
||||
@@ -24,7 +26,11 @@ http {
|
||||
client_header_timeout 300s;
|
||||
|
||||
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;
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
default $http_x_forwarded_proto;
|
||||
@@ -58,16 +64,25 @@ http {
|
||||
listen ${SSL_PORT} ssl;
|
||||
server_name _;
|
||||
|
||||
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;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root /app/data/acme-webroot;
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -75,31 +90,64 @@ http {
|
||||
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 ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
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 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 ~* \.map$ {
|
||||
return 404;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location ~ ^/users/sessions(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30001;
|
||||
proxy_http_version 1.1;
|
||||
@@ -126,7 +174,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/releases(/.*)?$ {
|
||||
@@ -135,7 +183,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/alerts(/.*)?$ {
|
||||
@@ -144,7 +192,34 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/alert-rules(/.*)?$ {
|
||||
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 ~ ^/notification-channels(/.*)?$ {
|
||||
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 ~ ^/alert-firings(/.*)?$ {
|
||||
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(/.*)?$ {
|
||||
@@ -153,7 +228,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/credentials(/.*)?$ {
|
||||
@@ -162,7 +237,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -170,6 +245,33 @@ http {
|
||||
}
|
||||
|
||||
location ~ ^/snippets(/.*)?$ {
|
||||
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 ~ ^/vault(/.*)?$ {
|
||||
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 ~ ^/termix-id(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
@@ -178,13 +280,34 @@ http {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location ~ ^/proxmox(/.*)?$ {
|
||||
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 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ~ ^/c2s-tunnel-presets(/.*)?$ {
|
||||
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 $scheme;
|
||||
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 ~ ^/terminal(/.*)?$ {
|
||||
@@ -193,7 +316,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
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 ~ ^/database(/.*)?$ {
|
||||
@@ -205,7 +346,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -224,7 +365,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -240,7 +381,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/quick-connect {
|
||||
@@ -252,7 +393,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/host/opkssh-chooser(/.*)?$ {
|
||||
@@ -267,7 +408,9 @@ http {
|
||||
|
||||
proxy_cache_bypass 1;
|
||||
proxy_no_cache 1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
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;
|
||||
}
|
||||
|
||||
location ~ ^/host/opkssh-callback(/.*)?$ {
|
||||
@@ -282,7 +425,9 @@ http {
|
||||
|
||||
proxy_cache_bypass 1;
|
||||
proxy_no_cache 1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
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;
|
||||
}
|
||||
|
||||
location /host/ {
|
||||
@@ -291,7 +436,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /session_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 /tailscale/ {
|
||||
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 /ssh/websocket/ {
|
||||
@@ -330,9 +493,9 @@ http {
|
||||
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -350,7 +513,16 @@ http {
|
||||
proxy_set_header Host $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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/session-sharing(/.*)?$ {
|
||||
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/tunnel/ {
|
||||
@@ -359,7 +531,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /ssh/tunnel/ {
|
||||
@@ -370,7 +542,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
@@ -383,7 +555,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/pinned {
|
||||
@@ -392,7 +564,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/shortcuts {
|
||||
@@ -401,7 +573,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/sudo-password {
|
||||
@@ -410,13 +582,15 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /ssh/file_manager/ {
|
||||
client_max_body_size 5G;
|
||||
client_body_timeout 300s;
|
||||
|
||||
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" always;
|
||||
|
||||
proxy_pass http://127.0.0.1:30004;
|
||||
@@ -424,7 +598,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -438,6 +612,8 @@ http {
|
||||
client_max_body_size 5G;
|
||||
client_body_timeout 300s;
|
||||
|
||||
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" always;
|
||||
|
||||
proxy_pass http://127.0.0.1:30004;
|
||||
@@ -445,7 +621,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -461,7 +637,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /health {
|
||||
@@ -470,7 +646,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/status(/.*)?$ {
|
||||
@@ -479,7 +655,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/metrics(/.*)?$ {
|
||||
@@ -488,7 +664,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
@@ -501,7 +677,20 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/host-metrics(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30005;
|
||||
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 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
location ~ ^/global-settings(/.*)?$ {
|
||||
@@ -510,7 +699,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/uptime(/.*)?$ {
|
||||
@@ -519,7 +708,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/activity(/.*)?$ {
|
||||
@@ -528,16 +717,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/dashboard/preferences(/.*)?$ {
|
||||
location ~ ^/service-links(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30006;
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/homepage(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30012;
|
||||
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 ^~ /docker/console/ {
|
||||
@@ -551,9 +749,9 @@ http {
|
||||
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -565,22 +763,59 @@ http {
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
}
|
||||
|
||||
# --- tmux-monitor begin ---
|
||||
location ~ ^/tmux_monitor(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30010;
|
||||
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;
|
||||
}
|
||||
# --- tmux-monitor end ---
|
||||
|
||||
location ~ ^/docker(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30007;
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location ^~ /serial/websocket/ {
|
||||
proxy_pass http://127.0.0.1:30011/;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 10s;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /app/html;
|
||||
internal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ 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;
|
||||
@@ -24,7 +26,11 @@ http {
|
||||
client_header_timeout 300s;
|
||||
|
||||
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;
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
default $http_x_forwarded_proto;
|
||||
@@ -49,14 +55,22 @@ http {
|
||||
|
||||
server {
|
||||
listen ${PORT};
|
||||
server_name localhost;
|
||||
server_name _;
|
||||
|
||||
absolute_redirect off;
|
||||
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root /app/data/acme-webroot;
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -64,31 +78,58 @@ http {
|
||||
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 ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
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;
|
||||
}
|
||||
|
||||
location / {
|
||||
root /app/html;
|
||||
index index.html index.htm;
|
||||
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 $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.map$ {
|
||||
return 404;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location ~ ^/users/sessions(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30001;
|
||||
proxy_http_version 1.1;
|
||||
@@ -115,7 +156,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/releases(/.*)?$ {
|
||||
@@ -124,7 +165,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/alerts(/.*)?$ {
|
||||
@@ -133,7 +174,34 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/alert-rules(/.*)?$ {
|
||||
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 ~ ^/notification-channels(/.*)?$ {
|
||||
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 ~ ^/alert-firings(/.*)?$ {
|
||||
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(/.*)?$ {
|
||||
@@ -142,7 +210,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/credentials(/.*)?$ {
|
||||
@@ -151,7 +219,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -159,6 +227,33 @@ http {
|
||||
}
|
||||
|
||||
location ~ ^/snippets(/.*)?$ {
|
||||
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 ~ ^/vault(/.*)?$ {
|
||||
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 ~ ^/termix-id(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
@@ -167,13 +262,34 @@ http {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location ~ ^/proxmox(/.*)?$ {
|
||||
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 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ~ ^/c2s-tunnel-presets(/.*)?$ {
|
||||
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 $scheme;
|
||||
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 ~ ^/terminal(/.*)?$ {
|
||||
@@ -182,7 +298,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
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 ~ ^/database(/.*)?$ {
|
||||
@@ -194,7 +328,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -213,7 +347,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -229,7 +363,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/quick-connect {
|
||||
@@ -241,7 +375,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/host/opkssh-chooser(/.*)?$ {
|
||||
@@ -256,7 +390,8 @@ http {
|
||||
|
||||
proxy_cache_bypass 1;
|
||||
proxy_no_cache 1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||
}
|
||||
|
||||
location ~ ^/host/opkssh-callback(/.*)?$ {
|
||||
@@ -271,7 +406,8 @@ http {
|
||||
|
||||
proxy_cache_bypass 1;
|
||||
proxy_no_cache 1;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||
}
|
||||
|
||||
location /host/ {
|
||||
@@ -280,7 +416,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /session_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 /tailscale/ {
|
||||
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 /ssh/websocket/ {
|
||||
@@ -319,9 +473,9 @@ http {
|
||||
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -339,7 +493,16 @@ http {
|
||||
proxy_set_header Host $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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/session-sharing(/.*)?$ {
|
||||
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/tunnel/ {
|
||||
@@ -348,7 +511,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /ssh/tunnel/ {
|
||||
@@ -359,7 +522,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
@@ -372,7 +535,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/pinned {
|
||||
@@ -381,7 +544,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/shortcuts {
|
||||
@@ -390,7 +553,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /host/file_manager/sudo-password {
|
||||
@@ -399,13 +562,14 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /ssh/file_manager/ {
|
||||
client_max_body_size 5G;
|
||||
client_body_timeout 300s;
|
||||
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
|
||||
|
||||
proxy_pass http://127.0.0.1:30004;
|
||||
@@ -413,7 +577,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -427,6 +591,7 @@ http {
|
||||
client_max_body_size 5G;
|
||||
client_body_timeout 300s;
|
||||
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
|
||||
|
||||
proxy_pass http://127.0.0.1:30004;
|
||||
@@ -434,7 +599,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
@@ -450,7 +615,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location /health {
|
||||
@@ -459,7 +624,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/status(/.*)?$ {
|
||||
@@ -468,7 +633,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/metrics(/.*)?$ {
|
||||
@@ -477,7 +642,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
@@ -490,7 +655,20 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/host-metrics(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30005;
|
||||
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 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
location ~ ^/global-settings(/.*)?$ {
|
||||
@@ -499,7 +677,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/uptime(/.*)?$ {
|
||||
@@ -508,7 +686,7 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/activity(/.*)?$ {
|
||||
@@ -517,16 +695,25 @@ http {
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/dashboard/preferences(/.*)?$ {
|
||||
location ~ ^/service-links(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30006;
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
}
|
||||
|
||||
location ~ ^/homepage(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30012;
|
||||
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 ^~ /docker/console/ {
|
||||
@@ -540,9 +727,9 @@ http {
|
||||
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -554,22 +741,59 @@ http {
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
}
|
||||
|
||||
# --- tmux-monitor begin ---
|
||||
location ~ ^/tmux_monitor(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30010;
|
||||
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;
|
||||
}
|
||||
# --- tmux-monitor end ---
|
||||
|
||||
location ~ ^/docker(/.*)?$ {
|
||||
proxy_pass http://127.0.0.1:30007;
|
||||
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 $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
location ^~ /serial/websocket/ {
|
||||
proxy_pass http://127.0.0.1:30011/;
|
||||
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;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_connect_timeout 10s;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /app/html;
|
||||
internal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# Database backends
|
||||
|
||||
Termix runs on SQLite by default. Postgres and MySQL are supported for
|
||||
self-hosted deployments; this document records how the three differ, because the
|
||||
differences are not only about SQL.
|
||||
|
||||
## This is multi-backend, not a migration
|
||||
|
||||
SQLite is not going away. The desktop app embeds its own backend and cannot ship
|
||||
a database server, so it will always run on SQLite. Postgres and MySQL exist for
|
||||
self-hosted deployments that need more than one process to reach the data —
|
||||
multiple replicas, an external backup story, or an existing database estate.
|
||||
|
||||
Anything that assumes a single engine is wrong.
|
||||
|
||||
## Where the schema comes from
|
||||
|
||||
`src/backend/database/db/schema.ts` is the single source of truth, written
|
||||
against `drizzle-orm/sqlite-core`.
|
||||
|
||||
`schema.pg.ts` and `schema.mysql.ts` are **generated** from it:
|
||||
|
||||
```bash
|
||||
npm run schema:generate # rewrite the generated modules
|
||||
npm run schema:check # fail if they are out of date (runs as part of lint)
|
||||
```
|
||||
|
||||
Never edit the generated files. `npm run lint` fails if they drift from the
|
||||
source, so a schema change that forgets to regenerate cannot reach main.
|
||||
|
||||
The transforms are mechanical:
|
||||
|
||||
| sqlite | postgres | mysql |
|
||||
| ------------------------------------------------ | ----------------- | ----------------------- |
|
||||
| `integer(…, { mode: "boolean" })` | `boolean` | `boolean` |
|
||||
| `integer(…).primaryKey({ autoIncrement: true })` | `serial` | `int().autoincrement()` |
|
||||
| `integer` | `integer` | `int` |
|
||||
| `real` | `doublePrecision` | `double` |
|
||||
| `text` used as a key | `varchar(255)` | `varchar(255)` |
|
||||
|
||||
A column becomes `varchar` if it is a primary key, is unique, or sits on either
|
||||
end of a foreign key — MySQL cannot index an unbounded `TEXT`, and both sides of
|
||||
a foreign key must agree.
|
||||
|
||||
## Durability
|
||||
|
||||
On SQLite the database is loaded into memory and serialised back to an encrypted
|
||||
file, so every write needs an explicit flush. That is what the `onWrite` hook
|
||||
each repository receives is for.
|
||||
|
||||
On Postgres and MySQL a committed write is already durable. No hook is installed
|
||||
at all — see `needsExplicitPersist` in `db/dialect.ts`.
|
||||
|
||||
## Encryption: what changes, and what does not
|
||||
|
||||
This is the part most likely to be misread, so it is spelled out.
|
||||
|
||||
### Unchanged on every backend
|
||||
|
||||
**Field-level encryption still applies.** Credentials and other sensitive values
|
||||
are encrypted in the application before they reach the database, under a
|
||||
per-user data key:
|
||||
|
||||
- `ssh_data` — passwords, private keys, key passphrases, sudo/RDP/VNC/Telnet
|
||||
secrets
|
||||
- `ssh_credentials` — passwords, private and public keys
|
||||
- `users` — TOTP secret and backup codes
|
||||
- `vault_tokens`, `opkssh_tokens`, `termix_identity_ca` — certificates and keys
|
||||
- `shared_host_secrets` — re-encrypted per recipient
|
||||
|
||||
Installation-level secrets — the OIDC client secret and LDAP bind password —
|
||||
are encrypted under the system key, since they have no owning user and must be
|
||||
readable during login.
|
||||
|
||||
This is the protection that matters most, and it is identical on all three
|
||||
engines.
|
||||
|
||||
### Different on Postgres and MySQL
|
||||
|
||||
**Whole-file encryption does not exist.** On SQLite the database file itself is
|
||||
encrypted at rest. There is no equivalent for a client-server engine: the data
|
||||
lives in the server's storage, not in a file Termix owns.
|
||||
|
||||
Concretely, on Postgres/MySQL the following are readable by anyone with database
|
||||
access, where on SQLite they were covered by the file encryption:
|
||||
|
||||
- host names, addresses, ports and usernames
|
||||
- folder and snippet names, and **snippet contents**
|
||||
- audit log entries
|
||||
- session recording metadata and paths
|
||||
- user names, roles and API key hashes
|
||||
|
||||
None of these are credentials — those stay encrypted — but together they
|
||||
describe your estate.
|
||||
|
||||
**If you run Postgres or MySQL, encryption at rest is your responsibility**:
|
||||
transparent data encryption, an encrypted volume, or an encrypted filesystem.
|
||||
Termix does not provide it and cannot.
|
||||
|
||||
### Threat model, side by side
|
||||
|
||||
| | SQLite | Postgres / MySQL |
|
||||
| ----------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------ |
|
||||
| Stolen database file / volume | credentials encrypted, everything else encrypted | credentials encrypted, **rest depends on your storage encryption** |
|
||||
| Database access without app access | credentials unreadable | credentials unreadable |
|
||||
| Application compromise while a user is unlocked | that user's secrets readable | same |
|
||||
| Backups | inherit file encryption | **plain unless you encrypt them** |
|
||||
|
||||
The second row is the point of field-level encryption, and it holds everywhere.
|
||||
The first and last rows are where the backends genuinely differ.
|
||||
|
||||
## Running on Postgres or MySQL
|
||||
|
||||
Two variables. Unset, nothing changes and SQLite is used exactly as before.
|
||||
|
||||
```
|
||||
DATABASE_DIALECT=postgres
|
||||
DATABASE_URL=postgres://user:password@host:5432/termix
|
||||
```
|
||||
|
||||
```
|
||||
DATABASE_DIALECT=mysql
|
||||
DATABASE_URL=mysql://user:password@host:3306/termix
|
||||
```
|
||||
|
||||
`mariadb://` is accepted for MySQL. The scheme is checked against the dialect
|
||||
before a connection is attempted, so a mismatch fails with a readable message
|
||||
rather than a driver error deep in a stack.
|
||||
|
||||
Point it at an **empty** database. Migrations are applied at startup, from
|
||||
`drizzle/postgres` or `drizzle/mysql`, and drizzle records what it has applied —
|
||||
so several instances against one database are safe, and so is restarting.
|
||||
|
||||
There is no migration path from an existing SQLite database. Exporting one and
|
||||
importing it into Postgres is not something this branch does.
|
||||
|
||||
### Docker
|
||||
|
||||
`drizzle/` ships in the image. A compose service needs only the two variables:
|
||||
|
||||
Added to the compose file in the README, that is one service and two variables:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
environment:
|
||||
PORT: "8080"
|
||||
DATABASE_DIALECT: postgres
|
||||
DATABASE_URL: postgres://termix:termix@db:5432/termix
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: termix
|
||||
POSTGRES_PASSWORD: termix
|
||||
POSTGRES_DB: termix
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
```
|
||||
|
||||
`DATA_DIR` is still used for uploads and recordings on every backend. Only the
|
||||
database itself moves.
|
||||
|
||||
## What is verified, and how
|
||||
|
||||
`npm run verify:dialect -- <url>` applies the migrations to an empty database and
|
||||
drives the real repository classes against it, asserting values rather than the
|
||||
absence of exceptions.
|
||||
|
||||
The repository test suite also runs against each engine:
|
||||
|
||||
```
|
||||
TEST_DIALECT=postgres TEST_DATABASE_URL=<url> npx vitest run \
|
||||
src/backend/tests/database/repositories --no-file-parallelism
|
||||
```
|
||||
|
||||
CI runs both, against PostgreSQL 16 and MySQL 8 service containers. Eighteen
|
||||
tests assert on bytes stored by the SQLite driver and skip on other engines;
|
||||
they still run in the SQLite pass.
|
||||
|
||||
Tested against PostgreSQL 16 and MySQL 8. **MariaDB is not a substitute for
|
||||
MySQL when testing** — it accepts DDL that MySQL 8 rejects, which has hidden a
|
||||
real defect here more than once.
|
||||
|
||||
### What neither of them covers
|
||||
|
||||
Both harnesses build a `DatabaseContext` of their own, so neither runs
|
||||
`createCurrentRepositoryContext()` — the one the application actually uses.
|
||||
That gap hid a hardcoded `dialect: "sqlite"` in it: every engine reported
|
||||
itself as SQLite at runtime while all three test passes stayed green, which on
|
||||
MySQL meant `upsert` reached for `onConflictDoUpdate` and died with a
|
||||
TypeError on the first write.
|
||||
|
||||
Anything the factory decides from the dialect needs its own test against the
|
||||
factory. Asserting it through a hand-built context proves nothing about what
|
||||
runs in production.
|
||||
|
||||
## Known limits
|
||||
|
||||
- The desktop app always uses SQLite. It embeds its own backend and cannot ship
|
||||
a database server.
|
||||
- Repositories import the SQLite table definitions on every engine. That is
|
||||
correct — the query builder needs identifiers and value encoders, and those
|
||||
agree — but it means `PortableDatabase` is a named approximation rather than a
|
||||
guarantee. See `repositories/database-context.ts`.
|
||||
- `getCurrentSettingValue` is a synchronous read. On Postgres and MySQL it comes
|
||||
from a cache primed at startup and kept current by `SettingsRepository`,
|
||||
because those drivers have no synchronous query.
|
||||
|
||||
That cache is per-process, so on a **multi-replica** deployment a setting
|
||||
changed on one instance does not reach the others through the write path. Each
|
||||
replica re-reads the settings table every 30 seconds
|
||||
(`SETTINGS_CACHE_REFRESH_SECONDS`, 0 to disable), which does not make settings
|
||||
immediately consistent — it bounds how long they can disagree. Changing a
|
||||
setting takes effect on the replica that made the change at once, and on the
|
||||
others within the interval.
|
||||
|
||||
- **Importing a backup is SQLite-only.** The restore writes tables in an order
|
||||
that is not dependency-safe and relies on `PRAGMA foreign_keys = OFF`, which
|
||||
has no equivalent here: Postgres needs superuser to disable triggers, and
|
||||
MySQL's session-scoped switch is not guaranteed across a pool. It refuses with
|
||||
a message rather than failing partway through and leaving a half-restored
|
||||
database. Restore into Postgres or MySQL with their own tooling.
|
||||
- **`LIKE` is case-insensitive on SQLite and case-sensitive on Postgres.** The
|
||||
four places that use it match folder path prefixes and settings keys, so the
|
||||
practical effect is that renaming a folder `prod` on SQLite also catches
|
||||
`PROD / api` and on Postgres does not. Postgres is arguably the more correct
|
||||
of the two; nothing was changed to make them agree, because that would alter
|
||||
SQLite behaviour for existing deployments.
|
||||
- The SQLite-era data migrations — legacy shared-credential cleanup, the
|
||||
shared-host-secrets rebuild, per-user field-encryption backfill — do not run on
|
||||
the other engines. A database created by the drizzle migrations never had the
|
||||
shapes they repair.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>إدارة SSH ذاتية الاستضافة والوصول إلى سطح المكتب البعيد</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
العربية ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix مجاني ومفتوح المصدر. إذا وجدته مفيدًا، فكّر في [التبرع](https://donate.termix.site/) للمساعدة في تغطية تكاليف الخادم ووقت التطوير.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>تم تحقيقه في 1 سبتمبر 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Termix هي منصة مفتوحة المصدر ومجانية للأبد وذاتية الاستضافة لإدارة الخوادم بشكل شامل. توفر حلاً متعدد المنصات لإدارة خوادمك وبنيتك التحتية من خلال واجهة واحدة وسهلة الاستخدام. يوفر Termix الوصول إلى طرفية SSH، والتحكم في سطح المكتب البعيد (RDP، VNC، Telnet)، وقدرات إنشاء أنفاق SSH، وإدارة ملفات SSH عن بُعد، والعديد من الأدوات الأخرى. يُعد Termix البديل المثالي المجاني وذاتي الاستضافة لـ Termius المتاح لجميع المنصات.
|
||||
|
||||
<br />
|
||||
|
||||
## الميزات
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**الوصول إلى طرفية SSH:**
|
||||
طرفية كاملة الميزات مع دعم تقسيم الشاشة (حتى 4 لوحات) مع نظام علامات تبويب شبيه بالمتصفح. يتضمن دعم تخصيص الطرفية بما في ذلك سمات الطرفية الشائعة والخطوط والمكونات الأخرى.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**الوصول إلى سطح المكتب البعيد:**
|
||||
دعم RDP و VNC و Telnet عبر المتصفح مع تخصيص كامل وتقسيم الشاشة.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**إدارة أنفاق SSH:**
|
||||
إنشاء وإدارة أنفاق SSH بين الخوادم مع إعادة الاتصال التلقائي ومراقبة الحالة وإعادة التوجيه المحلي أو البعيد أو SOCKS الديناميكي. يتم تخزين إعدادات نفق العميل-المكتبي إلى السيرفر محلياً لكل تثبيت مكتبي، ويمكن حفظ لقطات C2S الاختيارية على الخادم وإعادة تسميتها وتحميلها أو حذفها عندما تريد نقل تكوين النفق المحلي بين العملاء.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**مدير الملفات عن بُعد:**
|
||||
إدارة الملفات مباشرة على الخوادم البعيدة مع دعم عرض وتحرير الكود والصور والصوت والفيديو. رفع وتنزيل وإعادة تسمية وحذف ونقل الملفات بسلاسة مع دعم sudo. يتضمن دعم نقل الملفات من خادم إلى آخر.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**إدارة Docker و Podman:**
|
||||
تشغيل وإيقاف وتعليق وحذف الحاويات. عرض إحصائيات الحاويات. التحكم في الحاوية باستخدام طرفية docker exec. يدعم كلاً من Docker و Podman كبيئة تشغيل للحاويات. لم يُصمم ليحل محل Portainer أو Dockge بل لإدارة حاوياتك ببساطة مقارنة بإنشائها.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**مدير مضيفات SSH:**
|
||||
حفظ وتنظيم وإدارة اتصالات SSH الخاصة بك باستخدام العلامات والمجلدات (مع دعم تخصيص المجلدات والمجلدات المتداخلة)، وحفظ بيانات تسجيل الدخول القابلة لإعادة الاستخدام بسهولة مع إمكانية أتمتة نشر مفاتيح SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**مقاييس المضيف:**
|
||||
عرض استخدام المعالج والذاكرة والقرص والشبكة ووقت التشغيل ومعلومات النظام وجدار الحماية ومراقب المنافذ وعارض السجلات والمستخدمين/الصلاحيات والشهادات وغيرها الكثير، تعمل على معظم الخوادم المبنية على Linux. يتضمن رسوم بيانية تاريخية زمنية السلسلة وتنبيهات قائمة على الحدود مع دعم ntfy والـ webhook.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**مصادقة المستخدمين:**
|
||||
إدارة آمنة للمستخدمين مع ضوابط إدارية (يمكن تعديل معلومات المستخدمين الآخرين) ودعم OIDC/LDAP/SSO (مع التحكم في الوصول) و 2FA (TOTP) ودعم مفاتيح المرور (WebAuthn). عرض جلسات المستخدمين النشطة عبر جميع المنصات وإلغاء الصلاحيات. ربط حسابات OIDC/المحلية معاً. عرض سجل تدقيق لجميع إجراءات المستخدمين.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**تكامل Tailscale:**
|
||||
عرض أجهزة شبكتك من Tailscale لإضافتها بسرعة كمضيفات، والاتصال عبر Tailscale SSH كطريقة مصادقة، مما يتيح لقوائم تحكم الوصول في Tailscale التعامل مع التفويض دون الحاجة لتخزين بيانات اعتماد.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/المشاركة:**
|
||||
إنشاء الأدوار ومشاركة المضيفات عبر المستخدمين/الأدوار. يدعم جميع أنواع المصادقة وجميع بروتوكولات المضيف.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**الاتصالات التسلسلية:**
|
||||
الاتصال بالأجهزة التسلسلية (أجهزة التوجيه والمفاتيح والمتحكمات الدقيقة وغيرها) مباشرة من المتصفح أو تطبيق سطح المكتب. ضبط معدل نقل البيانات وبتات البيانات وبتات التوقف والتكافؤ. يستخدم Web Serial API في المتصفحات المدعومة أو خلفية أصلية في تطبيق Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**التنبيهات:**
|
||||
ضبط قواعد تنبيه قائمة على الحدود لمقاييس المضيف (المعالج والذاكرة والقرص وغيرها) والحصول على إشعارات عبر ntfy أو webhooks عند إطلاقها. عرض التنبيهات النشطة والمحلولة في سجل التاريخ.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**الصفحة الرئيسية:**
|
||||
صفحة رئيسية قابلة للتخصيص بالكامل مع شبكة أدوات قابلة للسحب والإفلات. أضف أدوات لحالة المضيف وروابط الخدمات والساعات والملاحظات وخلاصات RSS والطقس وحاويات Docker ومخططات مقاييس المضيف والطرفيات المضمنة والإطارات المضمنة وأكثر.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**تشفير قاعدة البيانات:**
|
||||
يُخزَّن الخادم الخلفي كملفات قاعدة بيانات SQLite مشفرة. اطلع على [الوثائق](https://docs.termix.site/security) لمزيد من المعلومات.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**الرسم البياني للشبكة:**
|
||||
تخصيص لوحة التحكم لتصور مختبرك المنزلي بناءً على اتصالات SSH مع دعم الحالة.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**أدوات SSH:**
|
||||
إنشاء مقتطفات أوامر قابلة لإعادة الاستخدام تُنفَّذ بنقرة واحدة. تشغيل أمر واحد في وقت واحد عبر عدة طرفيات مفتوحة.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**علامات التبويب الدائمة:**
|
||||
تبقى جلسات SSH وعلامات التبويب مفتوحة عبر الأجهزة/التحديثات إذا تم تفعيلها في ملف تعريف المستخدم.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**اللغات:**
|
||||
دعم مدمج لحوالي 30 لغة (تُدار بواسطة [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**مشاركة الجلسة:**
|
||||
شارك جلسة طرفية أو RDP أو VNC أو Telnet مباشرة مع الآخرين في الوقت الفعلي. شارك عبر رابط (الانضمام بشكل مجهول، بدون الحاجة لحساب) أو مع مستخدم Termix محدد، واختر الوصول للقراءة فقط أو للقراءة والكتابة. يمكن أن تنتهي صلاحية المشاركات تلقائيًا أو يتم إلغاؤها في أي وقت، ويمكن تبديل مشاركة الجلسة عالميًا أو لكل مضيف على حدة.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**تطبيق سطح مكتب مستقل + مزامنة ثنائية الاتجاه:**
|
||||
يعمل تطبيق سطح المكتب Electron بشكل مستقل تمامًا مع خلفية وقاعدة بيانات محلية خاصة به، دون الحاجة لخادم. يمكن اختياريًا توصيله بخادم Termix عن بُعد للمزامنة التلقائية ثنائية الاتجاه للمضيفين وبيانات الاعتماد والمقتطفات والمزيد، واختيار ما إذا كانت اتصالات SSH تبدأ محليًا أو عبر الخادم البعيد.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>المزيد من الميزات</b></summary>
|
||||
<br />
|
||||
|
||||
- **لوحة التحكم** - عرض معلومات الخادم بنظرة واحدة على لوحة التحكم
|
||||
- **مفاتيح API** - إنشاء مفاتيح API محددة النطاق للمستخدم مع تواريخ انتهاء صلاحية للاستخدام في الأتمتة/CI
|
||||
- **تصدير/استيراد البيانات** - تصدير واستيراد مضيفات SSH وبيانات الاعتماد وبيانات مدير الملفات
|
||||
- **إعداد SSL تلقائي** - إنشاء وإدارة شهادات SSL مدمجة مع إعادة التوجيه إلى HTTPS
|
||||
- **واجهة مستخدم حديثة** - واجهة نظيفة متوافقة مع سطح المكتب والهاتف المحمول مبنية بـ React و Tailwind CSS و Shadcn. الاختيار بين العديد من سمات واجهة المستخدم بما في ذلك الفاتح والداكن و Dracula وغيرها. استخدام مسارات URL لفتح أي اتصال في وضع ملء الشاشة.
|
||||
- **سجل الأوامر** - الإكمال التلقائي وعرض أوامر SSH التي تم تنفيذها سابقاً
|
||||
- **الاتصال السريع** - الاتصال بخادم دون الحاجة إلى حفظ بيانات الاتصال
|
||||
- **لوحة الأوامر** - اضغط مرتين على Shift الأيسر للوصول السريع إلى اتصالات SSH باستخدام لوحة المفاتيح
|
||||
- **تكامل Proxmox** - إضافة المضيفات تلقائياً إلى Termix من نسخة Proxmox الخاصة بك
|
||||
- **ميزات SSH الغنية** - دعم مضيفات القفز، Warpgate، الاتصالات المبنية على TOTP، SOCKS5، التحقق من مفتاح المضيف، الملء التلقائي لكلمة المرور، [OPKSSH](https://github.com/openpubkey/opkssh)، tmux، port knocking، تسجيل الطرفية، إعادة توجيه وكيل SSH، وكيل Bitwarden SSH، توقيع SSH عبر HashiCorp Vault، وغيرها.
|
||||
- **Termix ID** - مكافئ لـ sshid.io مدمج في Termix. احصل على اسم مستخدم، انشر مفاتيح SSH العامة الخاصة بك على رابط محلل (resolver URL)، واستخدم هيئة إصدار شهادات (CA) مدمجة لإصدار شهادات SSH.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## دعم المنصات
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">المنصة</th>
|
||||
<th align="center">التوزيع</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>أي متصفح حديث (Chrome، Safari، Firefox) · دعم PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>نسخة محمولة · مثبت MSI · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>نسخة محمولة · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## التثبيت
|
||||
|
||||
قم بزيارة [وثائق](https://docs.termix.site/install) Termix للحصول على تعليمات التثبيت الكاملة عبر جميع المنصات.
|
||||
|
||||
نموذج ملف Docker Compose (يمكنك حذف `guacd` والشبكة إذا كنت لا تخطط لاستخدام ميزات سطح المكتب البعيد):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## التبرع
|
||||
|
||||
Termix مجاني ومفتوح المصدر بدون اشتراكات أو خطط مدفوعة. إذا وجدته مفيدًا، فكّر في التبرع للمساعدة في تغطية تكاليف الخادم والنطاقات ووقت التطوير. تساعد التبرعات أيضاً في تمويل الوقت اللازم للبحث وتعلم ما هو مطلوب لبناء ميزات مثل SAML و Kubernetes ودعم الوكلاء (Agent). تابع التقدم وتبرع أدناه.
|
||||
|
||||
[تبرع](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## الرعاة
|
||||
|
||||
هل تريد إعلاناً مدفوعاً لدعم التطوير؟ راسلنا عبر البريد الإلكتروني [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## الدعم
|
||||
|
||||
إذا كنت بحاجة إلى مساعدة أو ترغب في طلب ميزة لـ Termix، قم بزيارة صفحة [المشكلات](https://github.com/Termix-SSH/Support/issues)، وسجل الدخول، واضغط على `New Issue`. يرجى أن تكون مفصلاً قدر الإمكان في مشكلتك، ويُفضَّل كتابتها باللغة الإنجليزية. يمكنك أيضاً الانضمام إلى خادم [Discord](https://discord.gg/jVQGdvHDrf) وزيارة قناة الدعم، ومع ذلك قد تكون أوقات الاستجابة أطول.
|
||||
|
||||
<br />
|
||||
|
||||
## لقطات الشاشة
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>شاهد نظرة عامة على التحديثات على YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>قد تكون بعض مقاطع الفيديو والصور قديمة أو قد لا تعرض الميزات بشكل مثالي.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## الميزات المخططة
|
||||
|
||||
راجع [المشاريع](https://github.com/orgs/Termix-SSH/projects/5) لعرض جميع الميزات المخططة. إذا كنت تتطلع للمساهمة، راجع [المساهمة](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## الترخيص
|
||||
|
||||
موزع بموجب رخصة Apache License الإصدار 2.0. راجع ملف `LICENSE` لمزيد من المعلومات.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>自托管 SSH 管理与远程桌面访问平台</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
中文 ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix 免费且开源。如果您觉得它有用,请考虑[捐赠](https://donate.termix.site/)以帮助支付服务器费用和开发时间。
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>获得于 2025年9月1日</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 概览
|
||||
|
||||
Termix 是一个开源、永久免费、自托管的一体化服务器管理平台。它提供了一个多平台解决方案,通过一个直观的界面管理你的服务器和基础设施。Termix 提供 SSH 终端访问、远程桌面控制(RDP、VNC、Telnet)、SSH 隧道功能、远程文件管理以及许多其他工具。Termix 是适用于所有平台的完美免费自托管 Termius 替代品。
|
||||
|
||||
<br />
|
||||
|
||||
## 功能
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 终端访问:**
|
||||
功能齐全的终端,支持分屏(最多 4 个面板),并配有类似浏览器的标签系统。包括对自定义终端的支持,如常用的终端主题、字体和其他组件。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**远程桌面访问:**
|
||||
通过浏览器支持 RDP、VNC 和 Telnet,具有完整的自定义和分屏功能。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 隧道管理:**
|
||||
创建和管理具有自动重连和健康监测功能的服务器间 SSH 隧道,支持本地、远程或动态 SOCKS 转发。桌面客户端到服务器的隧道设置按桌面安装本地存储,可选的 C2S 预设快照可保存到服务器、重命名、加载或删除,以便在客户端之间迁移本地隧道配置。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**远程文件管理器:**
|
||||
直接在远程服务器上管理文件,支持查看和编辑代码、图像、音频和视频。支持通过 sudo 无缝上传、下载、重命名、删除和移动文件。包括支持在服务器之间移动文件。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker 和 Podman 管理:**
|
||||
启动、停止、暂停、移除容器。查看容器统计信息。通过 docker exec 终端控制容器。同时支持 Docker 和 Podman 作为容器运行时。它的初衷不是取代 Portainer 或 Dockge,而是为了比直接创建容器更简单地管理它们。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 主机管理器:**
|
||||
通过标签和文件夹(支持文件夹自定义和嵌套文件夹)保存、组织和管理您的 SSH 连接,轻松保存可重用的登录信息,并能自动化部署 SSH 密钥。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**主机指标:**
|
||||
在大多数基于 Linux 的服务器上查看 CPU、内存、磁盘使用情况、网络、运行时间、系统信息、防火墙、端口监控、日志查看器、用户/权限、证书等更多信息。包括时间序列历史图表和支持 ntfy 与 webhook 的阈值告警。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**用户认证:**
|
||||
安全的用户管理,具有管理员控制(可编辑其他用户信息)和 OIDC/LDAP/SSO(带访问控制)、2FA (TOTP) 以及通行密钥(WebAuthn)支持。查看所有平台上的活动用户会话并撤销权限。将您的 OIDC/本地账户链接在一起。查看所有用户操作的审计日志。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale 集成:**
|
||||
列出您 Tailscale 网络中的设备以快速添加为主机,并使用 Tailscale SSH 作为身份验证方式,让您的 Tailscale ACL 处理授权而无需存储凭据。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/共享:**
|
||||
创建角色并在用户/角色之间共享主机。支持所有认证类型和所有主机协议。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**串口连接:**
|
||||
直接从浏览器或桌面应用连接到串口设备(路由器、交换机、微控制器等)。配置波特率、数据位、停止位和奇偶校验。在支持的浏览器中使用 Web Serial API,或在 Electron 应用中使用原生后端。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**告警:**
|
||||
为主机指标(CPU、内存、磁盘等)设置基于阈值的告警规则,并通过 ntfy 或 webhook 接收触发通知。在历史日志中查看触发和已解决的告警。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**主页:**
|
||||
具有拖放小组件网格的完全可定制主页。添加主机状态、服务链接、时钟、笔记、RSS 订阅、天气、Docker 容器、主机指标图表、嵌入式终端、iframe 等小组件。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**数据库加密:**
|
||||
后端存储为加密的 SQLite 数据库文件。查看[文档](https://docs.termix.site/security)了解更多。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**网络图:**
|
||||
自定义您的仪表板,根据您的 SSH 连接可视化您的家庭实验室,并支持状态监测。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 工具:**
|
||||
创建可重用的命令片段,只需点击一下即可执行。在多个打开的终端中同时运行一个命令。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**持久标签页:**
|
||||
如果在用户个人资料中启用,SSH 会话和标签页将在设备/刷新后保持打开状态。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**语言:**
|
||||
内置支持约 30 种语言(由 [Crowdin](https://docs.termix.site/translations) 管理)。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**会话共享:**
|
||||
与他人实时共享终端、RDP、VNC 或 Telnet 会话。通过链接分享(匿名加入,无需帐户)或与特定的 Termix 用户分享,并选择只读或读写权限。共享可以自动过期或随时撤销,会话共享可以全局或按主机切换。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**桌面独立运行 + 双向同步:**
|
||||
Electron 桌面应用可完全独立运行,拥有自己的本地后端和数据库,无需服务器。也可以选择连接到远程 Termix 服务器,实现主机、凭据、代码片段等的自动双向同步,并选择 SSH 连接是在本地启动还是通过远程服务器启动。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>更多功能</b></summary>
|
||||
<br />
|
||||
|
||||
- **仪表板** - 在仪表板上一目了然地查看服务器信息
|
||||
- **API 密钥** - 创建带有到期日期的用户范围 API 密钥,用于自动化/CI
|
||||
- **数据导出/导入** - 导出和导入 SSH 主机、凭据和文件管理器数据
|
||||
- **自动 SSL 设置** - 内置 SSL 证书生成和管理,支持 HTTPS 重定向
|
||||
- **现代 UI** - 使用 React、Tailwind CSS 和 Shadcn 构建的整洁的桌面/移动友好界面。有多种 UI 主题可选,包括浅色、深色、Dracula 等。使用 URL 路由全屏打开任何连接。
|
||||
- **命令历史** - 自动完成并查看之前运行过的 SSH 命令
|
||||
- **快速连接** - 无需保存连接数据即可连接到服务器
|
||||
- **命令面板** - 双击左 Shift 键即可通过键盘快速访问 SSH 连接
|
||||
- **Proxmox 集成** - 从您的 Proxmox 实例自动将主机添加到 Termix
|
||||
- **丰富的 SSH 功能** - 支持跳转主机、Warpgate、基于 TOTP 的连接、SOCKS5、主机密钥验证、密码自动填充、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、端口敲击、终端日志记录、SSH 代理转发、Bitwarden SSH 代理、HashiCorp Vault SSH 签名等
|
||||
- **Termix ID** - 内置于 Termix 中的 sshid.io 等效功能。认领一个用户名,在解析 URL 上发布您的公开 SSH 密钥,并使用内置 CA 签发 SSH 证书。
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## 平台支持
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">平台</th>
|
||||
<th align="center">发行版</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>任何现代浏览器(Chrome、Safari、Firefox)· PWA 支持</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>便携版 · MSI 安装程序 · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>便携版 · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## 安装
|
||||
|
||||
访问 [Termix 文档](https://docs.termix.site/install) 了解有关如何在所有平台上安装 Termix 的完整说明。
|
||||
|
||||
示例 Docker Compose 文件(如果您不打算使用远程桌面功能,可以省略 `guacd` 和网络部分):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## 捐赠
|
||||
|
||||
Termix 免费且开源,没有订阅或付费方案。如果您觉得它有用,请考虑捐赠以帮助支付服务器费用、域名和开发时间。捐赠还有助于资助研究和学习构建 SAML、Kubernetes 和 Agent 支持等功能所需的时间。在下方追踪进度并进行捐赠。
|
||||
|
||||
[捐赠](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## 赞助商
|
||||
|
||||
有意通过付费展示位置支持开发吗?请发送邮件至 [mail@termix.site](mailto:mail@termix.site)。
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 支持
|
||||
|
||||
如果您需要 Termix 的帮助或想要请求功能,请访问 [Issues](https://github.com/Termix-SSH/Support/issues) 页面,登录并点击 `New Issue`。请尽可能详细地描述您的问题,建议使用英语。您也可以加入 [Discord](https://discord.gg/jVQGdvHDrf) 服务器并访问支持频道,但响应时间可能较长。
|
||||
|
||||
<br />
|
||||
|
||||
## 展示
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>在 YouTube 上观看更新概览</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>某些视频和图像可能已过时,或者可能无法完美展示功能。</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 计划功能
|
||||
|
||||
查看 [Projects](https://github.com/orgs/Termix-SSH/projects/5) 了解所有计划功能。如果您想贡献代码,请参阅 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)。
|
||||
|
||||
<br />
|
||||
|
||||
## 许可证
|
||||
|
||||
根据 Apache License Version 2.0 发布。更多信息请参见 `LICENSE`。
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Selbst gehostete SSH-Verwaltung und Remote-Desktop-Zugriff</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
Deutsch ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix ist kostenlos und Open Source. Wenn Sie es nützlich finden, erwägen Sie eine [Spende](https://donate.termix.site/), um Serverkosten und Entwicklungszeit zu decken.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Erreicht am 1. September 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Uberblick
|
||||
|
||||
Termix ist eine quelloffene, dauerhaft kostenlose, selbst gehostete All-in-One-Serververwaltungsplattform. Sie bietet eine plattformubergreifende Losung zur Verwaltung Ihrer Server und Infrastruktur uber eine einzige, intuitive Oberflache. Termix bietet SSH-Terminalzugriff, Remote-Desktop-Steuerung (RDP, VNC, Telnet), SSH-Tunneling-Funktionen, Remote-Dateiverwaltung und viele weitere Werkzeuge. Termix ist die perfekte kostenlose und selbst gehostete Alternative zu Termius, verfugbar fur alle Plattformen.
|
||||
|
||||
<br />
|
||||
|
||||
## Funktionen
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH-Terminalzugriff:**
|
||||
Voll ausgestattetes Terminal mit Split-Screen-Unterstutzung (bis zu 4 Panels) mit einem browserahnlichen Tab-System. Enthalt Unterstutzung fur die Anpassung des Terminals einschliesslich gangiger Terminal-Themes, Schriftarten und anderer Komponenten.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Remote-Desktop-Zugriff:**
|
||||
RDP-, VNC- und Telnet-Unterstutzung uber den Browser mit vollstandiger Anpassung und Split-Screen.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH-Tunnelverwaltung:**
|
||||
Erstellen und verwalten Sie Server-zu-Server-SSH-Tunnel mit automatischer Wiederverbindung, Gesundheitsuberwachung sowie lokaler, entfernter oder dynamischer SOCKS-Weiterleitung. Desktop-Client-zu-Server-Tunneleinstellungen werden lokal pro Desktop-Installation gespeichert, optionale C2S-Preset-Snapshots konnen auf dem Server gespeichert, umbenannt, geladen oder geloscht werden, wenn Sie eine lokale Tunnelkonfiguration zwischen Clients ubertragen mochten.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Remote-Dateimanager:**
|
||||
Verwalten Sie Dateien direkt auf Remote-Servern mit Unterstutzung fur das Anzeigen und Bearbeiten von Code, Bildern, Audio und Video. Laden Sie Dateien hoch, herunter, benennen Sie sie um, loschen oder verschieben Sie sie nahtlos mit Sudo-Unterstutzung. Enthalt Unterstutzung fur das Verschieben von Dateien von Server zu Server.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker- und Podman-Verwaltung:**
|
||||
Container starten, stoppen, pausieren, entfernen. Container-Statistiken anzeigen. Container uber Docker-Exec-Terminal steuern. Unterstutzt sowohl Docker als auch Podman als Container-Laufzeitumgebung. Es wurde nicht entwickelt, um Portainer oder Dockge zu ersetzen, sondern um Ihre Container einfach zu verwalten, anstatt sie zu erstellen.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH-Host-Manager:**
|
||||
Speichern, organisieren und verwalten Sie Ihre SSH-Verbindungen mit Tags und Ordnern (Ordneranpassung und verschachtelte Ordner werden unterstutzt) und speichern Sie einfach wiederverwendbare Anmeldeinformationen mit der Moglichkeit, die Bereitstellung von SSH-Schlusseln zu automatisieren.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Host-Metriken:**
|
||||
CPU-, Arbeitsspeicher- und Festplattenauslastung, Netzwerk, Betriebszeit, Systeminformationen, Firewall, Port-Monitor, Log-Viewer, Benutzer/Berechtigungen, Zertifikate und vieles mehr anzeigen, was auf den meisten Linux-basierten Servern funktioniert. Enthalt Zeitreihen-Verlaufsdiagramme und schwellenwertbasierte Warnmeldungen mit ntfy- und Webhook-Unterstutzung.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Benutzerauthentifizierung:**
|
||||
Sichere Benutzerverwaltung mit Admin-Kontrollen (kann Informationen anderer Benutzer bearbeiten) und OIDC-/LDAP-/SSO-Unterstutzung (mit Zugriffskontrolle), 2FA (TOTP) und Passkey (WebAuthn)-Unterstutzung. Aktive Benutzersitzungen uber alle Plattformen anzeigen und Berechtigungen widerrufen. OIDC-/Lokale Konten miteinander verknupfen. Audit-Protokoll aller Benutzeraktionen anzeigen.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale-Integration:**
|
||||
Gerate aus Ihrem Tailnet auflisten, um sie schnell als Hosts hinzuzufugen, und mit Tailscale SSH als Authentifizierungsmethode verbinden, sodass Ihre Tailnet-ACLs die Autorisierung ubernehmen, ohne Anmeldedaten speichern zu mussen.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Freigabe:**
|
||||
Erstellen Sie Rollen und teilen Sie Hosts uber Benutzer/Rollen hinweg. Unterstutzt alle Authentifizierungstypen und alle Host-Protokolle.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Serielle Verbindungen:**
|
||||
Verbinden Sie sich direkt vom Browser oder der Desktop-App aus mit seriellen Geraten (Router, Switches, Mikrocontroller usw.). Konfigurieren Sie Baudrate, Datenbits, Stoppbits und Paritat. Verwendet die Web Serial API in unterstutzten Browsern oder ein natives Backend in der Electron-App.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Warnmeldungen:**
|
||||
Legen Sie schwellenwertbasierte Warnregeln fur Host-Metriken (CPU, Arbeitsspeicher, Festplatte usw.) fest und erhalten Sie Benachrichtigungen uber ntfy oder Webhooks, wenn diese ausgelost werden. Zeigen Sie ausgeloste und aufgeloste Warnmeldungen in einem Verlaufsprotokoll an.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Startseite:**
|
||||
Eine vollstandig anpassbare Startseite mit einem Drag-and-Drop-Widget-Raster. Fugen Sie Widgets fur Hoststatus, Service-Links, Uhren, Notizen, RSS-Feeds, Wetter, Docker-Container, Host-Metrik-Diagramme, eingebettete Terminals, iFrames und mehr hinzu.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Datenbankverschlusselung:**
|
||||
Backend gespeichert als verschlusselte SQLite-Datenbankdateien. Weitere Informationen in der [Dokumentation](https://docs.termix.site/security).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Netzwerkgraph:**
|
||||
Passen Sie Ihr Dashboard an, um Ihr Homelab basierend auf Ihren SSH-Verbindungen mit Statusunterstutzung zu visualisieren.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH-Werkzeuge:**
|
||||
Erstellen Sie wiederverwendbare Befehlsvorlagen, die mit einem einzigen Klick ausgefuhrt werden. Fuhren Sie einen Befehl gleichzeitig in mehreren geoffneten Terminals aus.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Persistente Tabs:**
|
||||
SSH-Sitzungen und Tabs bleiben uber Gerate/Aktualisierungen hinweg offen, wenn im Benutzerprofil aktiviert.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Sprachen:**
|
||||
Integrierte Unterstutzung fur ca. 30 Sprachen (verwaltet uber [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Sitzungsfreigabe:**
|
||||
Teilen Sie eine Live-Terminal-, RDP-, VNC- oder Telnet-Sitzung in Echtzeit mit anderen. Freigabe uber einen Link (anonymer Beitritt, kein Konto erforderlich) oder mit einem bestimmten Termix-Benutzer, mit Wahl zwischen Nur-Lese- oder Lese-/Schreibzugriff. Freigaben konnen automatisch ablaufen oder jederzeit widerrufen werden, und die Sitzungsfreigabe kann global oder pro Host umgeschaltet werden.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Eigenstandiger Desktop + bidirektionale Synchronisierung:**
|
||||
Die Electron-Desktop-App lauft vollstandig eigenstandig mit eigenem lokalem Backend und eigener Datenbank, kein Server erforderlich. Optional mit einem entfernten Termix-Server verbinden fur automatische bidirektionale Synchronisierung von Hosts, Zugangsdaten, Snippets und mehr, mit der Wahl, ob SSH-Verbindungen lokal oder uber den entfernten Server gestartet werden.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Weitere Funktionen</b></summary>
|
||||
<br />
|
||||
|
||||
- **Dashboard** - Serverinformationen auf einen Blick auf Ihrem Dashboard anzeigen
|
||||
- **API-Schlussel** - Erstellen Sie benutzerbezogene API-Schlussel mit Ablaufdaten zur Verwendung fur Automatisierung/CI
|
||||
- **Datenexport/-import** - SSH-Hosts, Anmeldeinformationen und Dateimanager-Daten exportieren und importieren
|
||||
- **Automatische SSL-Einrichtung** - Integrierte SSL-Zertifikatsgenerierung und -verwaltung mit HTTPS-Weiterleitungen
|
||||
- **Moderne Benutzeroberflache** - Saubere desktop-/mobilfreundliche Oberflache, erstellt mit React, Tailwind CSS und Shadcn. Wahlen Sie zwischen vielen verschiedenen UI-Themes einschliesslich Hell, Dunkel, Dracula usw. Verwenden Sie URL-Routen, um jede Verbindung im Vollbildmodus zu offnen.
|
||||
- **Befehlsverlauf** - Autovervollstandigung und Anzeige zuvor ausgefuhrter SSH-Befehle
|
||||
- **Schnellverbindung** - Verbinden Sie sich mit einem Server, ohne die Verbindungsdaten speichern zu mussen
|
||||
- **Befehlspalette** - Doppeltippen Sie die linke Umschalttaste, um schnell auf SSH-Verbindungen mit Ihrer Tastatur zuzugreifen
|
||||
- **Proxmox-Integration** - Automatisches Hinzufugen von Hosts zu Termix aus Ihrer Proxmox-Instanz
|
||||
- **SSH-Funktionsreich** - Unterstutzt Jump-Hosts, Warpgate, TOTP-basierte Verbindungen, SOCKS5, Host-Key-Verifizierung, automatisches Ausfullen von Passwortern, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, Port Knocking, Terminal-Protokollierung, SSH-Agent-Forwarding, Bitwarden SSH-Agent, HashiCorp Vault SSH-Signierung und mehr.
|
||||
- **Termix ID** - Ein sshid.io-Aquivalent, integriert in Termix. Beanspruchen Sie einen Handle, veroffentlichen Sie Ihre offentlichen SSH-Schlussel unter einer Resolver-URL und nutzen Sie eine integrierte CA zur Ausstellung von SSH-Zertifikaten.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Plattformunterstutzung
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Plattform</th>
|
||||
<th align="center">Distribution</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Jeder moderne Browser (Chrome, Safari, Firefox) · PWA-Unterstutzung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portabel · MSI-Installationsprogramm · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portabel · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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
|
||||
|
||||
Besuchen Sie die [Termix-Dokumentation](https://docs.termix.site/install) fur vollstandige Installationsanleitungen fur alle Plattformen.
|
||||
|
||||
Beispiel einer Docker-Compose-Datei (Sie konnen `guacd` und das Netzwerk weglassen, wenn Sie keine Remote-Desktop-Funktionen nutzen mochten):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Spenden
|
||||
|
||||
Termix ist kostenlos und Open Source, ohne Abonnements oder kostenpflichtige Plane. Wenn Sie es nutzlich finden, erwagen Sie eine Spende, um Serverkosten, Domains und Entwicklungszeit zu decken. Spenden helfen auch dabei, die Zeit zu finanzieren, die benotigt wird, um zu erforschen und zu lernen, was fur Funktionen wie SAML-, Kubernetes- und Agent-Unterstutzung erforderlich ist. Verfolgen Sie den Fortschritt und spenden Sie unten.
|
||||
|
||||
[Spenden](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Sponsoren
|
||||
|
||||
Interessiert an einer bezahlten Platzierung zur Unterstutzung der Entwicklung? Schreiben Sie eine E-Mail an [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Support
|
||||
|
||||
Wenn Sie Hilfe benotigen oder eine Funktion fur Termix anfragen mochten, besuchen Sie die [Issues](https://github.com/Termix-SSH/Support/issues)-Seite, melden Sie sich an und klicken Sie auf `New Issue`. Bitte beschreiben Sie Ihr Anliegen so detailliert wie moglich, vorzugsweise auf Englisch. Sie konnen auch dem [Discord](https://discord.gg/jVQGdvHDrf)-Server beitreten und den Support-Kanal besuchen, allerdings konnen die Antwortzeiten dort langer sein.
|
||||
|
||||
<br />
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Update-Ubersichten auf YouTube ansehen</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Einige Videos und Bilder konnen veraltet sein oder Funktionen moglicherweise nicht perfekt darstellen.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Geplante Funktionen
|
||||
|
||||
Siehe [Projekte](https://github.com/orgs/Termix-SSH/projects/5) fur alle geplanten Funktionen. Wenn Sie beitragen mochten, siehe [Mitwirken](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Lizenz
|
||||
|
||||
Verteilt unter der Apache License Version 2.0. Siehe `LICENSE` fur weitere Informationen.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Gestión SSH autoalojada y acceso a escritorio remoto</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
Español ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix es gratuito y de código abierto. Si lo encuentras útil, considera [donar](https://donate.termix.site/) para ayudar a cubrir los costos del servidor y el tiempo de desarrollo.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Logrado el 1 de septiembre de 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Descripcion General
|
||||
|
||||
Termix es una plataforma de gestion de servidores todo en uno, de codigo abierto, siempre gratuita y autoalojada. Proporciona una solucion multiplataforma para gestionar sus servidores e infraestructura a traves de una interfaz unica e intuitiva. Termix ofrece acceso a terminal SSH, control de escritorio remoto (RDP, VNC, Telnet), capacidades de tuneles SSH, gestion remota de archivos y muchas otras herramientas. Termix es la alternativa perfecta, gratuita y autoalojada a Termius, disponible para todas las plataformas.
|
||||
|
||||
<br />
|
||||
|
||||
## Caracteristicas
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acceso a Terminal SSH:**
|
||||
Terminal completo con soporte de pantalla dividida (hasta 4 paneles) con un sistema de pestanas similar al navegador. Incluye soporte para personalizar el terminal incluyendo temas comunes de terminal, fuentes y otros componentes.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acceso a Escritorio Remoto:**
|
||||
Soporte RDP, VNC y Telnet a traves del navegador con personalizacion completa y pantalla dividida.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestion de Tuneles SSH:**
|
||||
Cree y gestione tuneles SSH de servidor a servidor con reconexion automatica, monitoreo de estado y reenvio local, remoto o dinamico SOCKS. La configuracion de tuneles de cliente de escritorio a servidor se almacena localmente por instalacion de escritorio, los snapshots de presets C2S opcionales pueden guardarse en el servidor, renombrarse, cargarse o eliminarse cuando desee mover una configuracion de tunel local entre clientes.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestor Remoto de Archivos:**
|
||||
Gestione archivos directamente en servidores remotos con soporte para visualizar y editar codigo, imagenes, audio y video. Suba, descargue, renombre, elimine y mueva archivos sin problemas con soporte sudo. Incluye soporte para mover archivos de servidor a servidor.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestion de Docker y Podman:**
|
||||
Inicie, detenga, pause, elimine contenedores. Vea estadisticas de contenedores. Controle contenedores usando el terminal docker exec. Compatible con Docker y Podman como entorno de ejecucion de contenedores. No fue creado para reemplazar Portainer o Dockge, sino para simplemente gestionar sus contenedores en lugar de crearlos.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestor de Hosts SSH:**
|
||||
Guarde, organice y gestione sus conexiones SSH con etiquetas y carpetas (con personalizacion de carpetas y soporte de carpetas anidadas), y guarde facilmente informacion de inicio de sesion reutilizable con la capacidad de automatizar el despliegue de claves SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Metricas del Host:**
|
||||
Vea el uso de CPU, memoria y disco, red, tiempo de actividad, informacion del sistema, firewall, monitor de puertos, visor de registros, usuarios/permisos, certificados y muchos mas, que funcionan en la mayoria de los servidores basados en Linux. Incluye graficos de historial de series temporales y alertas basadas en umbrales con soporte para ntfy y webhooks.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Autenticacion de Usuarios:**
|
||||
Gestion segura de usuarios con controles de administrador (puede editar la informacion de otros usuarios) y soporte para OIDC/LDAP/SSO (con control de acceso), 2FA (TOTP) y soporte para passkeys (WebAuthn). Vea sesiones activas de usuarios en todas las plataformas y revoque permisos. Vincule sus cuentas OIDC/Locales entre si. Vea el registro de auditoria de las acciones de todos los usuarios.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Integracion con Tailscale:**
|
||||
Liste dispositivos de su red Tailscale para agregarlos rapidamente como hosts y conectese usando Tailscale SSH como metodo de autenticacion, permitiendo que las ACL de Tailscale gestionen la autorizacion sin almacenar credenciales.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Compartir:**
|
||||
Cree roles y comparta hosts entre usuarios/roles. Compatible con todos los tipos de autenticacion y todos los protocolos de host.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Conexiones Serie:**
|
||||
Conectese a dispositivos serie (routers, switches, microcontroladores, etc.) directamente desde el navegador o la aplicacion de escritorio. Configure la tasa de baudios, bits de datos, bits de parada y paridad. Utiliza la Web Serial API en navegadores compatibles o un backend nativo en la aplicacion Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Alertas:**
|
||||
Configure reglas de alerta basadas en umbrales para metricas del host (CPU, memoria, disco, etc.) y reciba notificaciones a traves de ntfy o webhooks cuando se activen. Vea las alertas activas y resueltas en un historial de registros.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Pagina de Inicio:**
|
||||
Una pagina de inicio completamente personalizable con una cuadricula de widgets de arrastrar y soltar. Agregue widgets para estado del host, enlaces de servicios, relojes, notas, feeds RSS, clima, contenedores Docker, graficos de metricas del host, terminales integrados, iframes y mas.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Cifrado de Base de Datos:**
|
||||
Backend almacenado como archivos de base de datos SQLite cifrados. Consulte la [documentacion](https://docs.termix.site/security) para mas informacion.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Grafico de Red:**
|
||||
Personalice su Dashboard para visualizar su homelab basado en sus conexiones SSH con soporte de estado.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Herramientas SSH:**
|
||||
Cree fragmentos de comandos reutilizables que se ejecutan con un solo clic. Ejecute un comando simultaneamente en multiples terminales abiertos.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Pestanas Persistentes:**
|
||||
Las sesiones SSH y pestanas permanecen abiertas entre dispositivos/actualizaciones si esta habilitado en el perfil de usuario.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Idiomas:**
|
||||
Soporte integrado para aproximadamente 30 idiomas (gestionado por [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Uso compartido de sesion:**
|
||||
Comparte una sesion en vivo de terminal, RDP, VNC o Telnet con otras personas en tiempo real. Comparte mediante un enlace (se une de forma anonima, sin necesidad de cuenta) o con un usuario especifico de Termix, y elige acceso de solo lectura o de lectura y escritura. Las comparticiones pueden expirar automaticamente o revocarse en cualquier momento, y el uso compartido de sesiones se puede activar globalmente o por host.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Aplicacion de escritorio independiente + sincronizacion bidireccional:**
|
||||
La aplicacion de escritorio Electron funciona de forma totalmente independiente con su propio backend y base de datos locales, sin necesidad de servidor. Opcionalmente, conectala a un servidor Termix remoto para sincronizacion bidireccional automatica de hosts, credenciales, fragmentos y mas, y elige si las conexiones SSH se inician localmente o a traves del servidor remoto.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Mas caracteristicas</b></summary>
|
||||
<br />
|
||||
|
||||
- **Dashboard** - Vea la informacion del servidor de un vistazo en su dashboard
|
||||
- **Claves API** - Cree claves API con ambito de usuario y fechas de vencimiento para usar en automatizacion/CI
|
||||
- **Exportacion/Importacion de Datos** - Exporte e importe hosts SSH, credenciales y datos del gestor de archivos
|
||||
- **Configuracion Automatica de SSL** - Generacion y gestion integrada de certificados SSL con redirecciones HTTPS
|
||||
- **Interfaz Moderna** - Interfaz limpia compatible con escritorio/movil construida con React, Tailwind CSS y Shadcn. Elija entre muchos temas de UI diferentes, incluyendo claro, oscuro, Dracula, etc. Use rutas URL para abrir cualquier conexion en pantalla completa.
|
||||
- **Historial de Comandos** - Autocompletado y visualizacion de comandos SSH ejecutados anteriormente
|
||||
- **Conexion Rapida** - Conectese a un servidor sin necesidad de guardar los datos de conexion
|
||||
- **Paleta de Comandos** - Pulse dos veces la tecla Shift izquierda para acceder rapidamente a las conexiones SSH con su teclado
|
||||
- **Integracion con Proxmox** - Agregue automaticamente hosts a Termix desde su instancia de Proxmox
|
||||
- **SSH Rico en Funciones** - Soporta jump hosts, Warpgate, conexiones basadas en TOTP, SOCKS5, verificacion de clave de host, autocompletado de contrasenas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registro de terminal, reenvio de agente SSH, agente SSH de Bitwarden, firma SSH con HashiCorp Vault y mas.
|
||||
- **Termix ID** - Un equivalente a sshid.io integrado en Termix. Reclame un identificador, publique sus claves publicas SSH en una URL de resolucion y use una CA integrada para emitir certificados SSH.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Soporte de Plataformas
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Plataforma</th>
|
||||
<th align="center">Distribucion</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Cualquier navegador moderno (Chrome, Safari, Firefox) · Soporte PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · Instalador MSI · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Instalacion
|
||||
|
||||
Visite la [documentacion de Termix](https://docs.termix.site/install) para obtener instrucciones completas de instalacion en todas las plataformas.
|
||||
|
||||
Archivo de ejemplo de Docker Compose (puede omitir `guacd` y la red si no planea usar las funciones de escritorio remoto):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Donar
|
||||
|
||||
Termix es gratuito y de codigo abierto, sin suscripciones ni planes de pago. Si lo encuentra util, considere donar para ayudar a cubrir los costos del servidor, los dominios y el tiempo de desarrollo. Las donaciones tambien ayudan a financiar el tiempo necesario para investigar y aprender lo que se necesita para construir funciones como soporte para SAML, Kubernetes y Agent. Siga el progreso y done a continuacion.
|
||||
|
||||
[Donar](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Patrocinadores
|
||||
|
||||
Interesado en un espacio patrocinado de pago para apoyar el desarrollo? Escriba a [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Soporte
|
||||
|
||||
Si necesita ayuda o desea solicitar una funcion para Termix, visite la pagina de [Issues](https://github.com/Termix-SSH/Support/issues), inicie sesion y pulse `New Issue`. Por favor, sea lo mas detallado posible en su reporte, preferiblemente escrito en ingles. Tambien puede unirse al servidor de [Discord](https://discord.gg/jVQGdvHDrf) y visitar el canal de soporte, sin embargo, los tiempos de respuesta pueden ser mas largos.
|
||||
|
||||
<br />
|
||||
|
||||
## Capturas de Pantalla
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Ver resúmenes de actualizaciones en YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Algunos videos e imagenes pueden estar desactualizados o no mostrar perfectamente las caracteristicas.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Caracteristicas Planeadas
|
||||
|
||||
Consulte [Proyectos](https://github.com/orgs/Termix-SSH/projects/5) para todas las caracteristicas planeadas. Si desea contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Licencia
|
||||
|
||||
Distribuido bajo la Licencia Apache Version 2.0. Consulte `LICENSE` para mas informacion.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Gestion SSH auto-hebergee et acces bureau a distance</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
Français ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix est gratuit et open source. Si vous le trouvez utile, pensez à [faire un don](https://donate.termix.site/) pour aider à couvrir les coûts de serveur et le temps de développement.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Obtenu le 1er septembre 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Presentation
|
||||
|
||||
Termix est une plateforme de gestion de serveurs tout-en-un, open source, a jamais gratuite et auto-hebergee. Elle fournit une solution multiplateforme pour gerer vos serveurs et votre infrastructure a travers une interface unique et intuitive. Termix offre un acces terminal SSH, le controle de bureau a distance (RDP, VNC, Telnet), des capacites de tunneling SSH, la gestion de fichiers SSH a distance et de nombreux autres outils. Termix est l'alternative parfaite, gratuite et auto-hebergee a Termius, disponible sur toutes les plateformes.
|
||||
|
||||
<br />
|
||||
|
||||
## Fonctionnalites
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acces terminal SSH:**
|
||||
Terminal complet avec support d'ecran partage (jusqu'a 4 panneaux) et un systeme d'onglets inspire des navigateurs. Inclut la personnalisation du terminal avec des themes courants, des polices et d'autres composants.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acces Bureau a Distance:**
|
||||
Support RDP, VNC et Telnet via navigateur avec personnalisation complete et ecran partage.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestion des tunnels SSH:**
|
||||
Creez et gerez des tunnels SSH de serveur a serveur avec reconnexion automatique, surveillance de l'etat et transfert local, distant ou SOCKS dynamique. Les parametres de tunnel client-bureau-vers-serveur sont stockes localement par installation bureau ; des instantanes de prereglages C2S optionnels peuvent etre sauvegardes sur le serveur, renommes, charges ou supprimes pour deplacer une configuration de tunnel locale entre clients.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestionnaire de fichiers distant:**
|
||||
Gerez les fichiers directement sur les serveurs distants avec support de la visualisation et de l'edition de code, images, audio et video. Televersez, telechargez, renommez, supprimez et deplacez des fichiers de maniere fluide avec support sudo. Inclut la prise en charge du deplacement de fichiers de serveur a serveur.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestion Docker et Podman:**
|
||||
Demarrez, arretez, mettez en pause, supprimez des conteneurs. Consultez les statistiques des conteneurs. Controlez les conteneurs via le terminal docker exec. Compatible avec Docker et Podman comme environnement d'execution de conteneurs. Non concu pour remplacer Portainer ou Dockge, mais plutot pour gerer simplement vos conteneurs plutot que de les creer.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestionnaire d'hotes SSH:**
|
||||
Enregistrez, organisez et gerez vos connexions SSH avec des tags et des dossiers (personnalisation des dossiers et prise en charge des dossiers imbriques), et sauvegardez facilement les informations de connexion reutilisables tout en automatisant le deploiement des cles SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Metriques d'hote:**
|
||||
Visualisez l'utilisation du CPU, de la memoire, du disque, le reseau, le temps de fonctionnement, les informations systeme, le pare-feu, le moniteur de ports, le visualiseur de journaux, les utilisateurs/permissions, les certificats et bien plus encore sur la plupart des serveurs Linux. Inclut des graphiques d'historique en serie temporelle et des alertes basees sur des seuils avec support ntfy et webhook.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Authentification des utilisateurs:**
|
||||
Gestion securisee des utilisateurs avec controles administrateur (peut modifier les informations des autres utilisateurs) et support OIDC/LDAP/SSO (avec controle d'acces), 2FA (TOTP), et support des passkeys (WebAuthn). Visualisez les sessions utilisateur actives sur toutes les plateformes et revoquez les permissions. Liez vos comptes OIDC/locaux ensemble. Consultez le journal d'audit des actions de tous les utilisateurs.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Integration Tailscale:**
|
||||
Listez les appareils de votre reseau Tailscale pour les ajouter rapidement comme hotes, et connectez-vous en utilisant Tailscale SSH comme methode d'authentification, laissant les ACL de votre reseau gerer l'autorisation sans stocker de credentials.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Partage:**
|
||||
Creez des roles et partagez des hotes entre utilisateurs/roles. Prend en charge tous les types d'authentification et tous les protocoles d'hote.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Connexions Serie:**
|
||||
Connectez-vous a des appareils serie (routeurs, commutateurs, microcontroleurs, etc.) directement depuis le navigateur ou l'application bureau. Configurez le debit en bauds, les bits de donnees, les bits d'arret et la parite. Utilise l'API Web Serial dans les navigateurs compatibles ou un backend natif dans l'application Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Alertes:**
|
||||
Definissez des regles d'alerte basees sur des seuils pour les metriques d'hote (CPU, memoire, disque, etc.) et recevez des notifications via ntfy ou webhooks lorsqu'elles se declenchent. Consultez les alertes actives et resolues dans un journal d'historique.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Page d'accueil:**
|
||||
Une page d'accueil entierement personnalisable avec une grille de widgets glisser-deposer. Ajoutez des widgets pour l'etat des hotes, les liens de services, les horloges, les notes, les flux RSS, la meteo, les conteneurs Docker, les graphiques de metriques d'hote, les terminaux integres, les iframes et plus encore.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Chiffrement de la base de donnees:**
|
||||
Le backend est stocke sous forme de fichiers de base de donnees SQLite chiffres. Consultez la [documentation](https://docs.termix.site/security) pour plus de details.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Graphe reseau:**
|
||||
Personnalisez votre tableau de bord pour visualiser votre homelab base sur vos connexions SSH avec support des statuts.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Outils SSH:**
|
||||
Creez des extraits de commandes reutilisables executables en un seul clic. Executez une commande simultanement sur plusieurs terminaux ouverts.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Onglets Persistants:**
|
||||
Les sessions SSH et les onglets restent ouverts sur tous les appareils/actualisations si active dans le profil utilisateur.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Langues:**
|
||||
Support integre d'environ 30 langues (gere par [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Partage de session:**
|
||||
Partagez une session de terminal, RDP, VNC ou Telnet en direct avec d'autres personnes en temps reel. Partagez via un lien (rejoint anonymement, sans compte necessaire) ou avec un utilisateur Termix specifique, et choisissez un acces en lecture seule ou en lecture-ecriture. Les partages peuvent expirer automatiquement ou etre revoques a tout moment, et le partage de session peut etre active globalement ou par hote.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Application de bureau autonome + synchronisation bidirectionnelle:**
|
||||
L'application de bureau Electron fonctionne de maniere totalement autonome avec son propre backend et sa propre base de donnees locale, sans serveur requis. Connectez-la eventuellement a un serveur Termix distant pour une synchronisation bidirectionnelle automatique des hotes, des identifiants, des extraits de code et plus encore, et choisissez si les connexions SSH sont demarrees localement ou via le serveur distant.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Plus de fonctionnalites</b></summary>
|
||||
<br />
|
||||
|
||||
- **Tableau de bord** - Consultez les informations de vos serveurs en un coup d'oeil depuis votre tableau de bord
|
||||
- **Cles API** - Creez des cles API a portee utilisateur avec des dates d'expiration pour une utilisation en automatisation/CI
|
||||
- **Export/Import de donnees** - Exportez et importez les hotes SSH, les identifiants et les donnees du gestionnaire de fichiers
|
||||
- **Configuration SSL automatique** - Generation et gestion integrees de certificats SSL avec redirections HTTPS
|
||||
- **Interface moderne** - Interface epuree compatible desktop/mobile construite avec React, Tailwind CSS et Shadcn. Choisissez parmi de nombreux themes d'interface utilisateur, notamment clair, sombre, Dracula, etc. Utilisez les routes URL pour ouvrir n'importe quelle connexion en plein ecran.
|
||||
- **Historique des commandes** - Auto-completion et consultation des commandes SSH precedemment executees
|
||||
- **Connexion rapide** - Connectez-vous a un serveur sans avoir a sauvegarder les donnees de connexion
|
||||
- **Palette de commandes** - Appuyez deux fois sur Shift gauche pour acceder rapidement aux connexions SSH avec votre clavier
|
||||
- **Integration Proxmox** - Ajoutez automatiquement des hotes dans Termix depuis votre instance Proxmox
|
||||
- **SSH riche en fonctionnalites** - Support des hotes de rebond, Warpgate, connexions basees sur TOTP, SOCKS5, verification des cles d'hote, remplissage automatique des mots de passe, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, journalisation du terminal, transfert d'agent SSH, agent SSH Bitwarden, signature SSH HashiCorp Vault, et plus encore.
|
||||
- **Termix ID** - Un equivalent de sshid.io integre a Termix. Reservez un identifiant, publiez vos cles SSH publiques a une URL de resolution, et utilisez une autorite de certification integree pour emettre des certificats SSH.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Support des plateformes
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Plateforme</th>
|
||||
<th align="center">Distribution</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Tout navigateur moderne (Chrome, Safari, Firefox) · Support PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · MSI Installateur · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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
|
||||
|
||||
Visitez la [documentation](https://docs.termix.site/install) de Termix pour des instructions d'installation completes sur toutes les plateformes.
|
||||
|
||||
Voici un exemple de fichier Docker Compose (vous pouvez omettre guacd et le reseau si vous ne prevoyez pas d'utiliser les fonctionnalites de bureau a distance) :
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Faire un don
|
||||
|
||||
Termix est gratuit et open source, sans abonnement ni plan payant. Si vous le trouvez utile, pensez a faire un don pour aider a couvrir les couts de serveur, les domaines et le temps de developpement. Les dons contribuent egalement a financer le temps necessaire pour rechercher et apprendre ce qui est requis pour construire des fonctionnalites comme SAML, Kubernetes et le support des agents. Suivez la progression et faites un don ci-dessous.
|
||||
|
||||
[Faire un don](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Sponsors
|
||||
|
||||
Interesse par un placement payant pour soutenir le developpement ? Envoyez un email a [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Support
|
||||
|
||||
Si vous avez besoin d'aide ou souhaitez demander une fonctionnalite pour Termix, visitez la page [Issues](https://github.com/Termix-SSH/Support/issues), connectez-vous et appuyez sur `New Issue`. Veuillez etre aussi detaille que possible dans votre issue, de preference redigee en anglais. Vous pouvez egalement rejoindre le serveur [Discord](https://discord.gg/jVQGdvHDrf) et visiter le canal de support, cependant les temps de reponse peuvent etre plus longs.
|
||||
|
||||
<br />
|
||||
|
||||
## Captures d'ecran
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Regarder les aperçus des mises a jour sur YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Certaines videos et images peuvent etre obsoletes ou ne pas presenter parfaitement les fonctionnalites.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Fonctionnalites prevues
|
||||
|
||||
Consultez les [Projects](https://github.com/orgs/Termix-SSH/projects/5) pour toutes les fonctionnalites prevues. Si vous souhaitez contribuer, consultez [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Licence
|
||||
|
||||
Distribue sous la licence Apache Version 2.0. Consultez `LICENSE` pour plus d'informations.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>स्व-होस्टेड SSH प्रबंधन और रिमोट डेस्कटॉप एक्सेस</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
हिन्दी ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix मुफ़्त और ओपन सोर्स है। यदि आपको यह उपयोगी लगता है, तो सर्वर लागत और विकास समय में मदद के लिए [दान करें](https://donate.termix.site/)।
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>1 सितंबर, 2025 को प्राप्त</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## अवलोकन
|
||||
|
||||
Termix एक ओपन-सोर्स, हमेशा के लिए मुफ़्त, सेल्फ-होस्टेड ऑल-इन-वन सर्वर प्रबंधन प्लेटफ़ॉर्म है। यह एक एकल, सहज इंटरफ़ेस के माध्यम से आपके सर्वर और बुनियादी ढाँचे के प्रबंधन के लिए एक मल्टी-प्लेटफ़ॉर्म समाधान प्रदान करता है। Termix SSH टर्मिनल एक्सेस, रिमोट डेस्कटॉप कंट्रोल (RDP, VNC, Telnet), SSH टनलिंग क्षमताएँ, रिमोट फ़ाइल प्रबंधन, और कई अन्य उपकरण प्रदान करता है। Termix सभी प्लेटफ़ॉर्म पर उपलब्ध Termius का सही मुफ़्त और सेल्फ-होस्टेड विकल्प है।
|
||||
|
||||
<br />
|
||||
|
||||
## विशेषताएँ
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH टर्मिनल एक्सेस:**
|
||||
ब्राउज़र जैसी टैब प्रणाली के साथ स्प्लिट-स्क्रीन सपोर्ट (4 पैनल तक) वाला पूर्ण-विशेषता वाला टर्मिनल। इसमें लोकप्रिय टर्मिनल थीम, फ़ॉन्ट और अन्य कंपोनेंट सहित टर्मिनल को कस्टमाइज़ करने का सपोर्ट शामिल है।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**रिमोट डेस्कटॉप एक्सेस:**
|
||||
ब्राउज़र पर RDP, VNC और Telnet सपोर्ट, पूर्ण कस्टमाइज़ेशन और स्प्लिट स्क्रीन के साथ।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH टनल प्रबंधन:**
|
||||
ऑटोमैटिक रीकनेक्शन, हेल्थ मॉनिटरिंग और लोकल, रिमोट या डायनेमिक SOCKS फॉरवर्डिंग के साथ सर्वर-टु-सर्वर SSH टनल बनाएँ और प्रबंधित करें। डेस्कटॉप क्लाइंट-टु-सर्वर टनल सेटिंग्स प्रत्येक डेस्कटॉप इंस्टॉल में स्थानीय रूप से संग्रहीत होती हैं; वैकल्पिक C2S प्रीसेट स्नैपशॉट सर्वर पर सेव किए जा सकते हैं, तथा जब आप किसी लोकल टनल कॉन्फ़िगरेशन को क्लाइंट के बीच स्थानांतरित करना चाहें तो उन्हें रीनेम, लोड या डिलीट किया जा सकता है।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**रिमोट फ़ाइल मैनेजर:**
|
||||
कोड, इमेज, ऑडियो और वीडियो देखने और संपादित करने के सपोर्ट के साथ रिमोट सर्वर पर सीधे फ़ाइलें प्रबंधित करें। sudo सपोर्ट के साथ फ़ाइलें अपलोड, डाउनलोड, रीनेम, डिलीट और मूव करें। इसमें फ़ाइलों को एक सर्वर से दूसरे सर्वर में स्थानांतरित करने का सपोर्ट भी शामिल है।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker और Podman प्रबंधन:**
|
||||
कंटेनर शुरू, बंद, पॉज़, हटाएँ। कंटेनर स्टैट्स देखें। docker exec टर्मिनल का उपयोग करके कंटेनर को नियंत्रित करें। Docker और Podman दोनों को कंटेनर रनटाइम के रूप में सपोर्ट करता है। इसे Portainer या Dockge की जगह लेने के लिए नहीं बनाया गया बल्कि कंटेनर बनाने की तुलना में उन्हें सरलता से प्रबंधित करने के लिए बनाया गया है।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH होस्ट मैनेजर:**
|
||||
टैग और फ़ोल्डर (फ़ोल्डर कस्टमाइज़ेशन और नेस्टेड फ़ोल्डर सपोर्ट के साथ) के साथ अपने SSH कनेक्शन सहेजें, व्यवस्थित करें और प्रबंधित करें, और SSH कुंजियों की तैनाती को स्वचालित करने की क्षमता के साथ पुन: उपयोग योग्य लॉगिन जानकारी आसानी से सहेजें।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**होस्ट मेट्रिक्स:**
|
||||
अधिकांश Linux आधारित सर्वर पर CPU, मेमोरी, डिस्क उपयोग, नेटवर्क, अपटाइम, सिस्टम जानकारी, फ़ायरवॉल, पोर्ट मॉनिटर, लॉग व्यूअर, उपयोगकर्ता/अनुमतियाँ, सर्टिफ़िकेट और भी बहुत कुछ देखें। इसमें टाइम-सीरीज़ हिस्ट्री ग्राफ़ और ntfy व webhook सपोर्ट के साथ थ्रेशोल्ड-आधारित अलर्ट शामिल हैं।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**उपयोगकर्ता प्रमाणीकरण:**
|
||||
व्यवस्थापक नियंत्रण (अन्य उपयोगकर्ताओं की जानकारी संपादित कर सकते हैं) और OIDC/LDAP/SSO (एक्सेस कंट्रोल के साथ), 2FA (TOTP), और पासकी (WebAuthn) सपोर्ट के साथ सुरक्षित उपयोगकर्ता प्रबंधन। सभी प्लेटफ़ॉर्म पर सक्रिय उपयोगकर्ता सत्र देखें और अनुमतियाँ रद्द करें। अपने OIDC/स्थानीय खातों को एक साथ जोड़ें। सभी उपयोगकर्ताओं की कार्रवाइयों का ऑडिट लॉग देखें।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale एकीकरण:**
|
||||
अपने Tailscale नेटवर्क के डिवाइस सूचीबद्ध करें ताकि उन्हें जल्दी से होस्ट के रूप में जोड़ा जा सके, और Tailscale SSH को प्रमाणीकरण विधि के रूप में उपयोग करके कनेक्ट करें, जिससे आपके Tailscale ACL क्रेडेंशियल संग्रहीत किए बिना प्राधिकरण संभाल सकें।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/शेयरिंग:**
|
||||
भूमिकाएँ बनाएँ और उपयोगकर्ताओं/भूमिकाओं में होस्ट साझा करें। सभी प्रमाणीकरण प्रकारों और सभी होस्ट प्रोटोकॉल का सपोर्ट करता है।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**सीरियल कनेक्शन:**
|
||||
सीरियल डिवाइस (राउटर, स्विच, माइक्रोकंट्रोलर आदि) से सीधे ब्राउज़र या डेस्कटॉप ऐप से कनेक्ट करें। बॉड रेट, डेटा बिट्स, स्टॉप बिट्स और पैरिटी कॉन्फ़िगर करें। समर्थित ब्राउज़र में Web Serial API या Electron ऐप में नेटिव बैकएंड का उपयोग करता है।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**अलर्ट:**
|
||||
होस्ट मेट्रिक्स (CPU, मेमोरी, डिस्क आदि) पर थ्रेशोल्ड-आधारित अलर्ट नियम सेट करें और जब वे ट्रिगर हों तो ntfy या webhooks के माध्यम से सूचना पाएँ। इतिहास लॉग में सक्रिय और हल किए गए अलर्ट देखें।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**होमपेज:**
|
||||
ड्रैग-एंड-ड्रॉप विजेट ग्रिड के साथ पूरी तरह से कस्टमाइज़ करने योग्य होमपेज। होस्ट स्टेटस, सर्विस लिंक, घड़ियाँ, नोट्स, RSS फ़ीड, मौसम, Docker कंटेनर, होस्ट मेट्रिक्स चार्ट, एम्बेडेड टर्मिनल, iframes और अन्य के लिए विजेट जोड़ें।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**डेटाबेस एन्क्रिप्शन:**
|
||||
बैकएंड एन्क्रिप्टेड SQLite डेटाबेस फ़ाइलों के रूप में संग्रहीत। अधिक जानकारी के लिए [डॉक्स](https://docs.termix.site/security) देखें।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**नेटवर्क ग्राफ़:**
|
||||
स्थिति सपोर्ट के साथ अपने SSH कनेक्शन के आधार पर अपने होमलैब को विज़ुअलाइज़ करने के लिए अपना डैशबोर्ड कस्टमाइज़ करें।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH टूल्स:**
|
||||
एक क्लिक से निष्पादित होने वाले पुन: उपयोग योग्य कमांड स्निपेट बनाएँ। एक साथ कई खुले टर्मिनलों में एक कमांड चलाएँ।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**परसिस्टेंट टैब:**
|
||||
उपयोगकर्ता प्रोफ़ाइल में सक्षम होने पर SSH सेशन और टैब डिवाइस/रीफ्रेश के पार खुले रहते हैं।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**भाषाएँ:**
|
||||
लगभग 30 भाषाओं का बिल्ट-इन सपोर्ट ([Crowdin](https://docs.termix.site/translations) द्वारा प्रबंधित)।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**सेशन शेयरिंग:**
|
||||
लाइव टर्मिनल, RDP, VNC, या Telnet सेशन को दूसरों के साथ रीयल टाइम में शेयर करें। लिंक के जरिए शेयर करें (गुमनाम रूप से जुड़ें, अकाउंट की जरूरत नहीं) या किसी खास Termix यूजर के साथ, और रीड-ओनली या रीड-राइट एक्सेस चुनें। शेयर अपने आप एक्सपायर हो सकते हैं या कभी भी रद्द किए जा सकते हैं, और सेशन शेयरिंग को ग्लोबली या प्रति होस्ट टॉगल किया जा सकता है।
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**डेस्कटॉप स्टैंडअलोन + 2-वे सिंक:**
|
||||
Electron डेस्कटॉप ऐप अपने खुद के लोकल बैकएंड और डेटाबेस के साथ पूरी तरह से स्टैंडअलोन चलता है, किसी सर्वर की जरूरत नहीं। चाहें तो इसे किसी रिमोट Termix सर्वर से कनेक्ट करें ताकि होस्ट्स, क्रेडेंशियल्स, स्निपेट्स और अन्य चीज़ों का ऑटोमैटिक 2-वे सिंक हो सके, और चुनें कि SSH कनेक्शन लोकली शुरू हों या रिमोट सर्वर के जरिए।
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>अधिक विशेषताएँ</b></summary>
|
||||
<br />
|
||||
|
||||
- **डैशबोर्ड** - अपने डैशबोर्ड पर एक नज़र में सर्वर की जानकारी देखें
|
||||
- **API कुंजियाँ** - ऑटोमेशन/CI के लिए उपयोग हेतु समाप्ति तिथियों के साथ उपयोगकर्ता-स्कोप्ड API कुंजियाँ बनाएँ
|
||||
- **डेटा एक्सपोर्ट/इम्पोर्ट** - SSH होस्ट, क्रेडेंशियल और फ़ाइल मैनेजर डेटा एक्सपोर्ट और इम्पोर्ट करें
|
||||
- **स्वचालित SSL सेटअप** - HTTPS रीडायरेक्ट के साथ बिल्ट-इन SSL सर्टिफ़िकेट जनरेशन और प्रबंधन
|
||||
- **आधुनिक UI** - React, Tailwind CSS, और Shadcn से बना साफ़ डेस्कटॉप/मोबाइल-फ़्रेंडली इंटरफ़ेस। लाइट, डार्क, ड्रैकुला आदि सहित कई अलग-अलग UI थीम के बीच चुनें। किसी भी कनेक्शन को फ़ुल-स्क्रीन में खोलने के लिए URL रूट का उपयोग करें।
|
||||
- **कमांड इतिहास** - पहले चलाए गए SSH कमांड का ऑटो-कम्प्लीट और दृश्य
|
||||
- **क्विक कनेक्ट** - कनेक्शन डेटा सहेजे बिना सर्वर से कनेक्ट करें
|
||||
- **कमांड पैलेट** - अपने कीबोर्ड से SSH कनेक्शन तक त्वरित पहुँच के लिए बाएँ Shift को दो बार टैप करें
|
||||
- **Proxmox एकीकरण** - अपने Proxmox इंस्टेंस से Termix में होस्ट स्वचालित रूप से जोड़ें
|
||||
- **SSH सुविधाओं से भरपूर** - जम्प होस्ट, Warpgate, TOTP आधारित कनेक्शन, SOCKS5, होस्ट की वेरिफ़िकेशन, पासवर्ड ऑटोफ़िल, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, पोर्ट नॉकिंग, टर्मिनल लॉगिंग, SSH एजेंट फ़ॉरवर्डिंग, Bitwarden SSH एजेंट, HashiCorp Vault SSH सिग्निंग, और अन्य का सपोर्ट।
|
||||
- **Termix ID** - Termix में बिल्ट-इन sshid.io के समकक्ष। एक हैंडल क्लेम करें, अपनी सार्वजनिक SSH कुंजियों को एक रिज़ॉल्वर URL पर प्रकाशित करें, और SSH सर्टिफ़िकेट जारी करने के लिए बिल्ट-इन CA का उपयोग करें।
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## प्लेटफ़ॉर्म सपोर्ट
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">प्लेटफ़ॉर्म</th>
|
||||
<th align="center">वितरण</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>कोई भी आधुनिक ब्राउज़र (Chrome, Safari, Firefox) · PWA सपोर्ट</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>पोर्टेबल · MSI इंस्टॉलर · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>पोर्टेबल · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## इंस्टॉलेशन
|
||||
|
||||
सभी प्लेटफ़ॉर्म पर पूर्ण इंस्टॉलेशन निर्देशों के लिए Termix [डॉक्स](https://docs.termix.site/install) पर जाएँ।
|
||||
|
||||
नमूना Docker Compose फ़ाइल (यदि आप रिमोट डेस्कटॉप सुविधाओं का उपयोग करने की योजना नहीं बना रहे हैं तो आप `guacd` और नेटवर्क को हटा सकते हैं):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## दान करें
|
||||
|
||||
Termix मुफ़्त और ओपन सोर्स है, बिना किसी सब्सक्रिप्शन या पेड प्लान के। यदि आपको यह उपयोगी लगता है, तो सर्वर लागत, डोमेन और विकास समय को कवर करने में मदद के लिए दान करने पर विचार करें। दान SAML, Kubernetes, और Agent सपोर्ट जैसी सुविधाओं के निर्माण के लिए आवश्यक शोध और सीखने में लगने वाले समय को वित्त पोषित करने में भी मदद करते हैं। नीचे प्रगति देखें और दान करें।
|
||||
|
||||
[दान करें](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## प्रायोजक
|
||||
|
||||
विकास को समर्थन देने के लिए पेड प्लेसमेंट में रुचि है? [mail@termix.site](mailto:mail@termix.site) पर ईमेल करें।
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## सहायता
|
||||
|
||||
यदि आपको सहायता चाहिए या Termix के लिए किसी विशेषता का अनुरोध करना चाहते हैं, तो [इश्यूज़](https://github.com/Termix-SSH/Support/issues) पेज पर जाएँ, लॉग इन करें, और `New Issue` दबाएँ। कृपया अपने इश्यू में यथासंभव विस्तृत विवरण दें, अधिमानतः अंग्रेज़ी में लिखें। आप [Discord](https://discord.gg/jVQGdvHDrf) सर्वर में भी शामिल हो सकते हैं और सहायता चैनल पर जा सकते हैं, हालाँकि, प्रतिक्रिया समय अधिक हो सकता है।
|
||||
|
||||
<br />
|
||||
|
||||
## स्क्रीनशॉट
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>YouTube पर अपडेट की समीक्षाएँ देखें</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>कुछ वीडियो और छवियाँ पुरानी हो सकती हैं या विशेषताओं को पूरी तरह से प्रदर्शित नहीं कर सकती हैं।</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## नियोजित विशेषताएँ
|
||||
|
||||
सभी नियोजित विशेषताओं के लिए [प्रोजेक्ट्स](https://github.com/orgs/Termix-SSH/projects/5) देखें। यदि आप योगदान देना चाहते हैं, तो [योगदान](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) देखें।
|
||||
|
||||
<br />
|
||||
|
||||
## लाइसेंस
|
||||
|
||||
Apache License Version 2.0 के तहत वितरित। अधिक जानकारी के लिए `LICENSE` देखें।
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Gestione SSH self-hosted e accesso al desktop remoto</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
Italiano
|
||||
</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>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://donate.termix.site/"><img alt="Donazioni di questo mese" src="https://img.shields.io/badge/dynamic/json?style=for-the-badge&label=Donazioni%20di%20questo%20mese&query=%24.fiatTotal&prefix=%24&url=https%3A%2F%2Ftermix.site%2Fdonation-snapshot.json&color=F39044&labelColor=1a1a1a" /></a>
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
Termix è gratuito e open source. Se lo trovi utile, considera di [donare](https://donate.termix.site/) per aiutare a coprire i costi del server e il tempo di sviluppo.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Ottenuto il 1 settembre 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Panoramica
|
||||
|
||||
Termix è una piattaforma di gestione server tutto-in-uno, open-source, per sempre gratuita e self-hosted. Fornisce una soluzione multipiattaforma per gestire i tuoi server e la tua infrastruttura attraverso un'unica interfaccia intuitiva. Termix offre accesso al terminale SSH, controllo remoto del desktop (RDP, VNC, Telnet), funzionalità di tunneling SSH, gestione remota dei file e molti altri strumenti. Termix è la perfetta alternativa gratuita e self-hosted a Termius, disponibile per tutte le piattaforme.
|
||||
|
||||
<br />
|
||||
|
||||
## Funzionalità
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Accesso Terminale SSH:**
|
||||
Terminale completo con supporto schermo diviso (fino a 4 pannelli) con un sistema di schede in stile browser. Include il supporto per la personalizzazione del terminale, inclusi temi, font e altri componenti comuni.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Accesso Desktop Remoto:**
|
||||
Supporto RDP, VNC e Telnet tramite browser con personalizzazione completa e schermo diviso.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestione Tunnel SSH:**
|
||||
Crea e gestisci tunnel SSH da server a server con riconnessione automatica, monitoraggio dello stato e inoltro locale, remoto o SOCKS dinamico. Le impostazioni del tunnel da client desktop a server sono archiviate localmente per ogni installazione desktop; gli snapshot di preset C2S opzionali possono essere salvati sul server, rinominati, caricati o eliminati per spostare una configurazione di tunnel locale tra i client.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestore File Remoto:**
|
||||
Gestisci i file direttamente sui server remoti con supporto per la visualizzazione e la modifica di codice, immagini, audio e video. Carica, scarica, rinomina, elimina e sposta file senza problemi con supporto sudo. Include il supporto per spostare file da server a server.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestione Docker e Podman:**
|
||||
Avvia, ferma, metti in pausa, rimuovi container. Visualizza le statistiche dei container. Controlla i container tramite terminale docker exec. Supporta sia Docker che Podman come runtime dei container. Non è stato creato per sostituire Portainer o Dockge, ma piuttosto per gestire semplicemente i tuoi container rispetto alla loro creazione.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gestore Host SSH:**
|
||||
Salva, organizza e gestisci le tue connessioni SSH con tag e cartelle (con personalizzazione delle cartelle e supporto per cartelle annidate), salva facilmente le informazioni di accesso riutilizzabili e automatizza il deployment delle chiavi SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Metriche Host:**
|
||||
Visualizza CPU, memoria, utilizzo del disco, rete, uptime, informazioni di sistema, firewall, monitoraggio porte, visualizzatore di log, utenti/permessi, certificati e molto altro, funzionanti sulla maggior parte dei server basati su Linux. Include grafici storici delle serie temporali e avvisi basati su soglie con supporto ntfy e webhook.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Autenticazione Utente:**
|
||||
Gestione utenti sicura con controlli amministrativi (può modificare le informazioni di altri utenti) e OIDC/LDAP/SSO (con controllo degli accessi), 2FA (TOTP) e supporto passkey (WebAuthn). Visualizza le sessioni utente attive su tutte le piattaforme e revoca i permessi. Collega i tuoi account OIDC/Locali tra loro. Visualizza il log di controllo delle azioni di tutti gli utenti.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Integrazione Tailscale:**
|
||||
Elenca i dispositivi della tua rete Tailscale per aggiungerli rapidamente come host, e connettiti utilizzando Tailscale SSH come metodo di autenticazione, lasciando che le ACL della tua rete gestiscano l'autorizzazione senza memorizzare credenziali.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Condivisione:**
|
||||
Crea ruoli e condividi host tra utenti/ruoli. Supporta tutti i tipi di autenticazione e tutti i protocolli host.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Connessioni Seriali:**
|
||||
Connettiti a dispositivi seriali (router, switch, microcontrollori, ecc.) direttamente dal browser o dall'app desktop. Configura baud rate, bit di dati, bit di stop e parità. Utilizza la Web Serial API nei browser supportati o un backend nativo nell'app Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Avvisi:**
|
||||
Imposta regole di avviso basate su soglie per le metriche dell'host (CPU, memoria, disco, ecc.) e ricevi notifiche tramite ntfy o webhook quando si attivano. Visualizza gli avvisi attivi e risolti in un registro storico.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Homepage:**
|
||||
Una homepage completamente personalizzabile con una griglia di widget drag-and-drop. Aggiungi widget per lo stato dell'host, link ai servizi, orologi, note, feed RSS, meteo, container Docker, grafici delle metriche dell'host, terminali incorporati, iframe e altro ancora.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Crittografia Database:**
|
||||
Il backend è archiviato come file di database SQLite crittografati. Consulta la [documentazione](https://docs.termix.site/security) per maggiori informazioni.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Grafico di Rete:**
|
||||
Personalizza la tua Dashboard per visualizzare il tuo homelab basato sulle connessioni SSH con supporto dello stato.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Strumenti SSH:**
|
||||
Crea snippet di comandi riutilizzabili che si eseguono con un singolo clic. Esegui un comando simultaneamente su più terminali aperti.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Schede Persistenti:**
|
||||
Le sessioni SSH e le schede rimangono aperte tra dispositivi/aggiornamenti se abilitato nel profilo utente.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Lingue:**
|
||||
Supporto integrato per circa 30 lingue (gestito da [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Condivisione Sessione:**
|
||||
Condividi una sessione di terminale, RDP, VNC o Telnet dal vivo con altri in tempo reale. Condividi tramite un link (accesso anonimo, nessun account necessario) o con un utente Termix specifico, e scegli l'accesso in sola lettura o lettura/scrittura. Le condivisioni possono scadere automaticamente o essere revocate in qualsiasi momento, e la condivisione della sessione puo essere attivata globalmente o per singolo host.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**App Desktop Standalone + Sincronizzazione Bidirezionale:**
|
||||
L'app desktop Electron funziona in modo completamente autonomo con il proprio backend e database locali, senza bisogno di un server. Facoltativamente, collegala a un server Termix remoto per la sincronizzazione bidirezionale automatica di host, credenziali, snippet e altro, scegliendo se le connessioni SSH vengono avviate localmente o tramite il server remoto.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Altre funzionalità</b></summary>
|
||||
<br />
|
||||
|
||||
- **Dashboard** - Visualizza le informazioni del server a colpo d'occhio sulla tua dashboard
|
||||
- **Chiavi API** - Crea chiavi API con ambito utente e date di scadenza da utilizzare per automazione/CI
|
||||
- **Esportazione/Importazione Dati** - Esporta e importa host SSH, credenziali e dati del gestore file
|
||||
- **Configurazione SSL Automatica** - Generazione e gestione integrata dei certificati SSL con reindirizzamenti HTTPS
|
||||
- **Interfaccia Moderna** - Interfaccia pulita e responsive per desktop/mobile costruita con React, Tailwind CSS e Shadcn. Scegli tra molti temi UI diversi, inclusi chiaro, scuro, Dracula, ecc. Usa i percorsi URL per aprire qualsiasi connessione a schermo intero.
|
||||
- **Cronologia Comandi** - Autocompletamento e visualizzazione dei comandi SSH eseguiti in precedenza
|
||||
- **Connessione Rapida** - Connettiti a un server senza dover salvare i dati di connessione
|
||||
- **Palette Comandi** - Premi due volte shift sinistro per accedere rapidamente alle connessioni SSH con la tastiera
|
||||
- **Integrazione Proxmox** - Aggiungi automaticamente host a Termix dalla tua istanza Proxmox
|
||||
- **SSH Ricco di Funzionalità** - Supporta jump host, Warpgate, connessioni basate su TOTP, SOCKS5, verifica chiave host, compilazione automatica password, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registrazione terminale, SSH agent forwarding, Bitwarden SSH agent, firma SSH HashiCorp Vault e altro ancora
|
||||
- **Termix ID** - L'equivalente di sshid.io integrato in Termix. Rivendica un handle, pubblica le tue chiavi SSH pubbliche su un URL resolver e utilizza una CA integrata per emettere certificati SSH
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Supporto Piattaforme
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Piattaforma</th>
|
||||
<th align="center">Distribuzione</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Qualsiasi browser moderno (Chrome, Safari, Firefox) · Supporto PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · Installer MSI · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Installazione
|
||||
|
||||
Visita la [Documentazione Termix](https://docs.termix.site/install) per le istruzioni complete di installazione su tutte le piattaforme.
|
||||
|
||||
File Docker Compose di esempio (puoi omettere `guacd` e la rete se non prevedi di utilizzare le funzioni di desktop remoto):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Dona
|
||||
|
||||
Termix è gratuito e open source, senza abbonamenti o piani a pagamento. Se lo trovi utile, considera di donare per aiutare a coprire i costi del server, i domini e il tempo di sviluppo. Le donazioni aiutano anche a finanziare il tempo necessario per ricercare e imparare ciò che serve per costruire funzionalità come SAML, Kubernetes e supporto Agent. Segui i progressi e dona qui sotto.
|
||||
|
||||
[Dona](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Sponsor
|
||||
|
||||
Interessato a un posizionamento a pagamento per supportare lo sviluppo? Scrivi a [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Supporto
|
||||
|
||||
Se hai bisogno di aiuto o vuoi richiedere una funzionalità per Termix, visita la pagina [Issues](https://github.com/Termix-SSH/Support/issues), accedi e premi `New Issue`. Per favore, sii il più dettagliato possibile nella tua segnalazione, preferibilmente scritta in inglese. Puoi anche unirti al server [Discord](https://discord.gg/jVQGdvHDrf) e visitare il canale di supporto, tuttavia i tempi di risposta potrebbero essere più lunghi.
|
||||
|
||||
<br />
|
||||
|
||||
## Screenshot
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Guarda le panoramiche degli aggiornamenti su YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Alcuni video e immagini potrebbero non essere aggiornati o potrebbero non mostrare perfettamente le funzionalità.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Funzionalità Pianificate
|
||||
|
||||
Consulta [Projects](https://github.com/orgs/Termix-SSH/projects/5) per tutte le funzionalità pianificate. Se desideri contribuire, consulta [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Licenza
|
||||
|
||||
Distribuito sotto la Licenza Apache Versione 2.0. Consulta `LICENSE` per maggiori informazioni.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>セルフホスト型 SSH 管理とリモートデスクトップアクセス</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
日本語 ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix は無料のオープンソースプロジェクトです。便利だと感じた場合は、サーバーコストと開発時間のために[寄付](https://donate.termix.site/)をご検討ください。
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>2025年9月1日に達成</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 概要
|
||||
|
||||
Termixは、オープンソースで永久無料のセルフホスト型オールインワンサーバー管理プラットフォームです。単一の直感的なインターフェースを通じて、サーバーとインフラストラクチャを管理するマルチプラットフォームソリューションを提供します。Termixは、SSHターミナルアクセス、リモートデスクトップ制御(RDP、VNC、Telnet)、SSHトンネリング機能、リモートファイル管理、およびその他多くのツールを提供します。Termixは、すべてのプラットフォームで利用可能なTermiusの完全無料でセルフホスト可能な代替ソリューションです。
|
||||
|
||||
<br />
|
||||
|
||||
## 機能
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSHターミナルアクセス:**
|
||||
ブラウザ風タブシステムによる分割画面対応(最大4パネル)のフル機能ターミナル。一般的なターミナルテーマ、フォント、その他のコンポーネントを含むターミナルカスタマイズに対応しています。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**リモートデスクトップアクセス:**
|
||||
ブラウザ上でRDP、VNC、Telnetをサポート、完全なカスタマイズと分割画面に対応しています。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSHトンネル管理:**
|
||||
自動再接続とヘルスモニタリング、ローカル・リモート・ダイナミックSOCKSフォワーディングを備えたサーバー間SSHトンネルの作成・管理が可能です。デスクトップクライアント対サーバーのトンネル設定はデスクトップインストールごとにローカルに保存され、オプションのC2Sプリセットスナップショットをサーバーに保存・名前変更・読み込み・削除してクライアント間でローカルトンネル設定を移動できます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**リモートファイルマネージャー:**
|
||||
コード、画像、音声、動画の表示・編集に対応し、リモートサーバー上のファイルを直接管理できます。sudo対応でファイルのアップロード、ダウンロード、名前変更、削除、移動をシームレスに実行できます。サーバー間でのファイル移動にも対応しています。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**DockerおよびPodman管理:**
|
||||
コンテナの起動、停止、一時停止、削除。コンテナの統計情報を表示。docker execターミナルでコンテナを操作。DockerとPodmanの両方をコンテナランタイムとしてサポートしています。PortainerやDockgeの代替ではなく、コンテナの作成よりも簡易的な管理を目的としています。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSHホストマネージャー:**
|
||||
タグやフォルダ(フォルダのカスタマイズとネストフォルダ対応)でSSH接続を保存、整理、管理し、再利用可能なログイン情報を簡単に保存しながらSSHキーのデプロイを自動化できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**ホストメトリクス:**
|
||||
ほとんどのLinuxベースのサーバーで、CPU、メモリ、ディスク使用量、ネットワーク、アップタイム、システム情報、ファイアウォール、ポートモニター、ログビューア、ユーザー/権限、証明書など、さらに多くの情報を表示できます。時系列の履歴グラフと、ntfyおよびwebhookに対応したしきい値ベースのアラートを含みます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**ユーザー認証:**
|
||||
管理者コントロール(他のユーザー情報を編集可能)とOIDC/LDAP/SSO(アクセス制御付き)、2FA(TOTP)、パスキー(WebAuthn)対応による安全なユーザー管理。すべてのプラットフォームでアクティブなユーザーセッションを表示し、権限を取り消し可能。OIDC/ローカルアカウントの連携が可能です。すべてのユーザー操作の監査ログを表示できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscaleインテグレーション:**
|
||||
Tailnetのデバイスをリストしてホストとしてすばやく追加し、Tailscale SSHを認証方法として使用して接続します。これにより、TailnetのACLが認証情報を保存せずに認可を処理します。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/共有:**
|
||||
ロールを作成し、ユーザー/ロール間でホストを共有できます。すべての認証タイプとすべてのホストプロトコルに対応しています。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**シリアル接続:**
|
||||
ブラウザまたはデスクトップアプリからシリアルデバイス(ルーター、スイッチ、マイクロコントローラーなど)に直接接続できます。ボーレート、データビット、ストップビット、パリティを設定できます。対応ブラウザではWeb Serial APIを使用し、Electronアプリではネイティブバックエンドを使用します。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**アラート:**
|
||||
ホストメトリクス(CPU、メモリ、ディスクなど)に対してしきい値ベースのアラートルールを設定し、発動時にntfyまたはwebhookで通知を受け取れます。発動中および解決済みのアラートを履歴ログで確認できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**ホームページ:**
|
||||
ドラッグ&ドロップのウィジェットグリッドを備えた完全カスタマイズ可能なホームページ。ホストステータス、サービスリンク、時計、メモ、RSSフィード、天気、Dockerコンテナ、ホストメトリクスグラフ、埋め込みターミナル、iframeなどのウィジェットを追加できます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**データベース暗号化:**
|
||||
バックエンドは暗号化されたSQLiteデータベースファイルとして保存されます。詳細は[ドキュメント](https://docs.termix.site/security)をご覧ください。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**ネットワークグラフ:**
|
||||
ダッシュボードをカスタマイズして、SSH接続に基づくホームラボのネットワークをステータス表示付きで可視化できます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSHツール:**
|
||||
ワンクリックで実行できる再利用可能なコマンドスニペットの作成。複数の開いているターミナルに対して同時にコマンドを実行できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**永続タブ:**
|
||||
ユーザープロフィールで有効にすると、SSHセッションとタブがデバイス/更新をまたいで開いたまま保持されます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**多言語対応:**
|
||||
約30言語の組み込みサポート([Crowdin](https://docs.termix.site/translations)で管理されています)。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**セッション共有:**
|
||||
ターミナル、RDP、VNC、Telnetのライブセッションを他のユーザーとリアルタイムで共有できます。リンクで共有(匿名参加、アカウント不要)するか、特定のTermixユーザーと共有し、読み取り専用または読み取り/書き込みアクセスを選択できます。共有は自動的に期限切れになるか、いつでも取り消すことができ、セッション共有はグローバルまたはホストごとに切り替えられます。
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**デスクトップスタンドアロン + 双方向同期:**
|
||||
Electronデスクトップアプリは、独自のローカルバックエンドとデータベースを使用して完全にスタンドアロンで動作し、サーバーは不要です。オプションでリモートのTermixサーバーに接続し、ホスト、認証情報、スニペットなどの自動双方向同期を行い、SSH接続をローカルで開始するかリモートサーバー経由で開始するかを選択できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>その他の機能</b></summary>
|
||||
<br />
|
||||
|
||||
- **ダッシュボード** - ダッシュボードでサーバー情報を一目で確認できます
|
||||
- **APIキー** - 自動化/CI用に有効期限付きのユーザースコープAPIキーを作成できます
|
||||
- **データのエクスポート/インポート** - SSHホスト、認証情報、ファイルマネージャーデータのエクスポートとインポートが可能です
|
||||
- **自動SSL設定** - HTTPSリダイレクト付きの組み込みSSL証明書生成・管理が可能です
|
||||
- **モダンUI** - React、Tailwind CSS、Shadcnで構築された、デスクトップ/モバイル対応のクリーンなインターフェース。ライト、ダーク、Draculaなど、多くの異なるUIテーマから選択可能。URLルートで任意の接続をフルスクリーンで開くことができます。
|
||||
- **コマンド履歴** - 過去に実行したSSHコマンドの自動補完と表示が可能です
|
||||
- **クイック接続** - 接続データを保存せずにサーバーに接続できます
|
||||
- **コマンドパレット** - 左Shiftキーを2回押すことで、キーボードからSSH接続に素早くアクセスできます
|
||||
- **Proxmox統合** - Proxmoxインスタンスからホストを自動的にTermixに追加できます
|
||||
- **SSH機能充実** - ジャンプホスト、Warpgate、TOTPベースの接続、SOCKS5、ホストキー検証、パスワード自動入力、[OPKSSH](https://github.com/openpubkey/opkssh)、tmux、ポート敲き(port knocking)、ターミナルログ記録、SSHエージェントフォワーディング、Bitwarden SSHエージェント、HashiCorp Vault SSH署名などに対応しています
|
||||
- **Termix ID** - Termixに組み込まれたsshid.io相当の機能です。ハンドルを取得し、リゾルバーURLで公開SSHキーを公開し、組み込みCAを使用してSSH証明書を発行できます。
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## プラットフォーム対応
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">プラットフォーム</th>
|
||||
<th align="center">配布形式</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>あらゆる最新ブラウザ(Chrome、Safari、Firefox)· PWA対応</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>ポータブル版 · MSIインストーラー · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>ポータブル版 · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## インストール
|
||||
|
||||
すべてのプラットフォームへのTermixのインストール方法については、[Termixドキュメント](https://docs.termix.site/install)をご覧ください。
|
||||
|
||||
サンプルDocker Composeファイル(リモートデスクトップ機能を使用する予定がない場合は、`guacd`とネットワークの設定を省略できます):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## 寄付
|
||||
|
||||
Termixは無料のオープンソースプロジェクトであり、サブスクリプションや有料プランはありません。便利だと感じた場合は、サーバーコスト、ドメイン、開発時間を賄うための寄付をご検討ください。寄付は、SAML、Kubernetes、Agentサポートなどの機能を構築するために必要な調査と学習の時間を確保することにも役立ちます。以下で進捗を確認し、寄付できます。
|
||||
|
||||
[寄付する](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## スポンサー
|
||||
|
||||
開発を支援するための有料掲載にご興味がありますか?[mail@termix.site](mailto:mail@termix.site)までメールをお送りください。
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## サポート
|
||||
|
||||
Termixに関するヘルプや機能リクエストが必要な場合は、[Issues](https://github.com/Termix-SSH/Support/issues)ページにアクセスし、ログインして`New Issue`を押してください。Issueはできるだけ詳細に記述し、英語での記述が望ましいです。また、[Discord](https://discord.gg/jVQGdvHDrf)サーバーに参加してサポートチャンネルを利用することもできますが、応答時間が長くなる場合があります。
|
||||
|
||||
<br />
|
||||
|
||||
## スクリーンショット
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>YouTubeでアップデートの概要を視聴する</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>動画や画像の一部は最新ではない場合や、機能を完全に紹介できていない場合があります。</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 予定されている機能
|
||||
|
||||
すべての予定機能については[Projects](https://github.com/orgs/Termix-SSH/projects/5)をご覧ください。コントリビュートをご希望の方は[Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)をご覧ください。
|
||||
|
||||
<br />
|
||||
|
||||
## ライセンス
|
||||
|
||||
Apache License Version 2.0のもとで配布されています。詳細は`LICENSE`をご覧ください。
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>셀프 호스팅 SSH 관리 및 원격 데스크톱 액세스</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
한국어 ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix는 무료 오픈소스 프로젝트입니다. 유용하게 사용하고 있다면 서버 비용과 개발 시간을 위해 [후원](https://donate.termix.site/)을 고려해 주세요.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>2025년 9월 1일 달성</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 개요
|
||||
|
||||
Termix는 오픈 소스이며 영구 무료인 셀프 호스팅 올인원 서버 관리 플랫폼입니다. 단일 직관적인 인터페이스를 통해 서버와 인프라를 관리할 수 있는 멀티 플랫폼 솔루션을 제공합니다. Termix는 SSH 터미널 접속, 원격 데스크톱 제어(RDP, VNC, Telnet), SSH 터널링 기능, 원격 SSH 파일 관리 및 기타 다양한 도구를 제공합니다. Termix는 모든 플랫폼에서 사용 가능한 Termius의 완벽한 무료 셀프 호스팅 대안입니다.
|
||||
|
||||
<br />
|
||||
|
||||
## 기능
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 터미널 접속:**
|
||||
브라우저 스타일 탭 시스템과 분할 화면 지원(최대 4개 패널)을 갖춘 완전한 기능의 터미널. 일반 터미널 테마, 글꼴 및 기타 구성 요소를 포함한 터미널 사용자 정의 지원.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**원격 데스크톱 접속:**
|
||||
완전한 사용자 정의와 분할 화면을 지원하는 브라우저 기반 RDP, VNC, Telnet 지원.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 터널 관리:**
|
||||
자동 재연결, 상태 모니터링, 로컬·원격·동적 SOCKS 포워딩을 지원하는 서버 간 SSH 터널 생성 및 관리. 데스크톱 클라이언트-서버 터널 설정은 데스크톱 설치별로 로컬에 저장되며, 선택적 C2S 사전 설정 스냅샷을 서버에 저장·이름 변경·불러오기·삭제하여 클라이언트 간 로컬 터널 구성을 이동할 수 있습니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**원격 파일 관리자:**
|
||||
코드, 이미지, 오디오, 비디오의 보기 및 편집을 지원하여 원격 서버에서 파일을 직접 관리. sudo 지원으로 파일 업로드, 다운로드, 이름 변경, 삭제, 이동을 원활하게 수행. 서버 간 파일 이동도 지원합니다.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker 및 Podman 관리:**
|
||||
컨테이너 시작, 중지, 일시 정지, 제거. 컨테이너 통계 보기. docker exec 터미널로 컨테이너 제어. Docker와 Podman을 모두 컨테이너 런타임으로 지원. Portainer나 Dockge를 대체하기 위한 것이 아니라 컨테이너 생성보다는 간편한 관리를 목적으로 합니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 호스트 관리자:**
|
||||
태그와 폴더(폴더 사용자 지정 및 중첩 폴더 지원)로 SSH 연결을 저장, 정리, 관리하고, 재사용 가능한 로그인 정보를 쉽게 저장하면서 SSH 키 배포를 자동화.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**호스트 메트릭:**
|
||||
대부분의 Linux 기반 서버에서 CPU, 메모리, 디스크 사용량, 네트워크, 업타임, 시스템 정보, 방화벽, 포트 모니터, 로그 뷰어, 사용자/권한, 인증서 등 다양한 정보를 표시. 시계열 히스토리 그래프와 ntfy 및 웹훅을 지원하는 임계값 기반 알림을 포함합니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**사용자 인증:**
|
||||
관리자 제어(다른 사용자 정보 편집 가능)와 OIDC/LDAP/SSO(액세스 제어 포함), 2FA(TOTP), 패스키(WebAuthn) 지원을 통한 안전한 사용자 관리. 모든 플랫폼에서 활성 사용자 세션을 보고 권한을 취소 가능. OIDC/로컬 계정 연동. 모든 사용자 작업의 감사 로그 조회.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale 통합:**
|
||||
Tailscale 네트워크의 기기를 나열하여 호스트로 빠르게 추가하고, Tailscale SSH를 인증 방법으로 사용하여 연결함으로써 자격 증명을 저장하지 않고도 네트워크 ACL이 권한 부여를 처리하도록 합니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/공유:**
|
||||
역할을 생성하고 사용자/역할 간에 호스트를 공유합니다. 모든 인증 유형과 모든 호스트 프로토콜을 지원합니다.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**시리얼 연결:**
|
||||
브라우저 또는 데스크톱 앱에서 직접 시리얼 장치(라우터, 스위치, 마이크로컨트롤러 등)에 연결. 보드레이트, 데이터 비트, 스톱 비트, 패리티 구성. 지원 브라우저에서는 Web Serial API를, Electron 앱에서는 네이티브 백엔드를 사용합니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**알림:**
|
||||
호스트 메트릭(CPU, 메모리, 디스크 등)에 대한 임계값 기반 알림 규칙을 설정하고 트리거될 때 ntfy 또는 웹훅을 통해 알림 수신. 기록 로그에서 발생 중인 알림과 해결된 알림 확인.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**홈페이지:**
|
||||
드래그 앤 드롭 위젯 그리드를 갖춘 완전 맞춤형 홈페이지. 호스트 상태, 서비스 링크, 시계, 메모, RSS 피드, 날씨, Docker 컨테이너, 호스트 메트릭 차트, 임베디드 터미널, iframe 등의 위젯 추가 가능.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**데이터베이스 암호화:**
|
||||
백엔드가 암호화된 SQLite 데이터베이스 파일로 저장됨. 자세한 내용은 [문서](https://docs.termix.site/security)를 참조하세요.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**네트워크 그래프:**
|
||||
대시보드를 사용자 정의하여 SSH 연결 기반의 홈랩 네트워크를 상태 표시와 함께 시각화.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH 도구:**
|
||||
한 번의 클릭으로 실행 가능한 재사용 가능 명령어 스니펫 생성. 여러 열린 터미널에서 동시에 하나의 명령어 실행.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**지속 탭:**
|
||||
사용자 프로필에서 활성화된 경우 SSH 세션 및 탭이 기기/새로 고침 간에 열린 상태 유지.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**다국어 지원:**
|
||||
약 30개 언어 내장 지원([Crowdin](https://docs.termix.site/translations)으로 관리).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**세션 공유:**
|
||||
터미널, RDP, VNC, Telnet 세션을 다른 사람과 실시간으로 공유하세요. 링크를 통해 공유(계정 없이 익명으로 참여)하거나 특정 Termix 사용자와 공유할 수 있으며, 읽기 전용 또는 읽기/쓰기 권한을 선택할 수 있습니다. 공유는 자동으로 만료되거나 언제든지 취소될 수 있으며, 세션 공유는 전역 또는 호스트별로 전환할 수 있습니다.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**데스크톱 독립 실행 + 양방향 동기화:**
|
||||
Electron 데스크톱 앱은 자체 로컬 백엔드와 데이터베이스를 사용하여 서버 없이 완전히 독립적으로 실행됩니다. 선택적으로 원격 Termix 서버에 연결하여 호스트, 자격 증명, 스니펫 등을 자동으로 양방향 동기화하고, SSH 연결을 로컬에서 시작할지 원격 서버를 통해 시작할지 선택할 수 있습니다.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>더 많은 기능</b></summary>
|
||||
<br />
|
||||
|
||||
- **대시보드** - 대시보드에서 서버 정보를 한눈에 확인
|
||||
- **API 키** - 자동화/CI에 사용할 만료일이 있는 사용자 범위 API 키 생성
|
||||
- **데이터 내보내기/가져오기** - SSH 호스트, 자격 증명, 파일 관리자 데이터의 내보내기 및 가져오기
|
||||
- **자동 SSL 설정** - HTTPS 리디렉션을 포함한 내장 SSL 인증서 생성 및 관리
|
||||
- **모던 UI** - React, Tailwind CSS, Shadcn으로 구축된 깔끔한 데스크톱/모바일 친화적 인터페이스. 라이트, 다크, 드라큘라 등 다양한 UI 테마 선택 가능. URL 라우트를 사용하여 모든 연결을 전체 화면으로 열기 가능.
|
||||
- **명령어 기록** - 이전에 실행한 SSH 명령어의 자동 완성 및 조회
|
||||
- **빠른 연결** - 연결 데이터를 저장하지 않고 서버에 접속
|
||||
- **명령어 팔레트** - 왼쪽 Shift 키를 두 번 눌러 키보드로 SSH 연결에 빠르게 접근
|
||||
- **Proxmox 통합** - Proxmox 인스턴스에서 Termix로 호스트를 자동 추가
|
||||
- **풍부한 SSH 기능** - 점프 호스트, Warpgate, TOTP 기반 연결, SOCKS5, 호스트 키 검증, 비밀번호 자동 입력, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, 포트 노킹, 터미널 로깅, SSH 에이전트 포워딩, Bitwarden SSH 에이전트, HashiCorp Vault SSH 서명 등 지원.
|
||||
- **Termix ID** - Termix에 내장된 sshid.io와 동등한 기능. 핸들을 등록하고, 리졸버 URL에 공개 SSH 키를 게시하며, 내장 CA를 사용하여 SSH 인증서를 발급할 수 있습니다.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## 플랫폼 지원
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">플랫폼</th>
|
||||
<th align="center">배포판</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>모든 최신 브라우저(Chrome, Safari, Firefox) · PWA 지원</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>포터블 · MSI 설치 프로그램 · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>포터블 · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## 설치
|
||||
|
||||
모든 플랫폼에 Termix를 설치하는 방법에 대한 자세한 내용은 Termix [문서](https://docs.termix.site/install)를 방문하세요.
|
||||
|
||||
다음은 Docker Compose 파일 예시입니다(원격 데스크톱 기능을 사용할 계획이 없다면 guacd와 네트워크를 생략할 수 있습니다):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## 후원
|
||||
|
||||
Termix는 구독이나 유료 요금제가 없는 무료 오픈소스 프로젝트입니다. 유용하게 사용하고 있다면 서버 비용, 도메인, 개발 시간을 위해 후원을 고려해 주세요. 후원은 SAML, Kubernetes, 에이전트 지원과 같은 기능을 구축하는 데 필요한 사항을 연구하고 학습하는 시간에도 사용됩니다. 아래에서 진행 상황을 확인하고 후원할 수 있습니다.
|
||||
|
||||
[후원하기](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## 스폰서
|
||||
|
||||
개발 지원을 위한 유료 광고에 관심이 있으신가요? [mail@termix.site](mailto:mail@termix.site)로 이메일을 보내주세요.
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 지원
|
||||
|
||||
Termix에 대한 도움이 필요하거나 기능을 요청하려면 [Issues](https://github.com/Termix-SSH/Support/issues) 페이지를 방문하여 로그인하고 `New Issue`를 누르세요. 이슈는 가능한 한 상세하게 작성하고, 영어로 작성하는 것이 좋습니다. [Discord](https://discord.gg/jVQGdvHDrf) 서버에 참여하여 지원 채널을 이용할 수도 있지만, 응답 시간이 더 길 수 있습니다.
|
||||
|
||||
<br />
|
||||
|
||||
## 스크린샷
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>YouTube에서 업데이트 개요 시청하기</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>일부 비디오 및 이미지는 최신이 아니거나 기능을 완벽하게 보여주지 않을 수 있습니다.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## 계획된 기능
|
||||
|
||||
모든 계획된 기능은 [Projects](https://github.com/orgs/Termix-SSH/projects/5)를 참조하세요. 기여를 원하시면 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)을 참조하세요.
|
||||
|
||||
<br />
|
||||
|
||||
## 라이선스
|
||||
|
||||
Apache License Version 2.0에 따라 배포됩니다. 자세한 내용은 `LICENSE`를 참조하세요.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Gerenciamento SSH auto-hospedado e acesso a area de trabalho remota</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
Português ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix é gratuito e de código aberto. Se o achar útil, considere [doar](https://donate.termix.site/) para ajudar a cobrir os custos de servidor e o tempo de desenvolvimento.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Conquistado em 1 de setembro de 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Visao Geral
|
||||
|
||||
Termix e uma plataforma de gerenciamento de servidores tudo-em-um, de codigo aberto, sempre gratuita e auto-hospedada. Ela fornece uma solucao multiplataforma para gerenciar seus servidores e infraestrutura atraves de uma interface unica e intuitiva. Termix oferece acesso a terminal SSH, controle de desktop remoto (RDP, VNC, Telnet), capacidades de tunelamento SSH, gerenciamento remoto de arquivos SSH e muitas outras ferramentas. Termix e a alternativa perfeita, gratuita e auto-hospedada ao Termius, disponivel para todas as plataformas.
|
||||
|
||||
<br />
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acesso ao Terminal SSH:**
|
||||
Terminal completo com suporte a tela dividida (ate 4 paineis) com um sistema de abas similar ao navegador. Inclui suporte para personalizacao do terminal incluindo temas comuns de terminal, fontes e outros componentes.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Acesso a Area de Trabalho Remota:**
|
||||
Suporte a RDP, VNC e Telnet pelo navegador com personalizacao completa e tela dividida.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gerenciamento de Tuneis SSH:**
|
||||
Crie e gerencie tuneis SSH de servidor para servidor com reconexao automatica, monitoramento de saude e encaminhamento local, remoto ou SOCKS dinamico. As configuracoes de tunel de cliente desktop para servidor sao armazenadas localmente por instalacao de desktop, snapshots de predefinicoes C2S opcionais podem ser salvos no servidor, renomeados, carregados ou excluidos quando voce quiser mover uma configuracao de tunel local entre clientes.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gerenciador Remoto de Arquivos:**
|
||||
Gerencie arquivos diretamente em servidores remotos com suporte para visualizar e editar codigo, imagens, audio e video. Faca upload, download, renomeie, exclua e mova arquivos facilmente com suporte sudo. Inclui suporte para mover arquivos de servidor para servidor.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gerenciamento de Docker e Podman:**
|
||||
Inicie, pare, pause, remova conteineres. Visualize estatisticas de conteineres. Controle conteineres usando o terminal Docker Exec. Suporta Docker e Podman como ambiente de execucao de conteineres. Nao foi feito para substituir Portainer ou Dockge, mas sim para simplesmente gerenciar seus conteineres em vez de cria-los.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Gerenciador de Hosts SSH:**
|
||||
Salve, organize e gerencie suas conexoes SSH com tags e pastas (com personalizacao de pastas e suporte a pastas aninhadas), e salve facilmente informacoes de login reutilizaveis com a capacidade de automatizar a implantacao de chaves SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Metricas do Host:**
|
||||
Visualize o uso de CPU, memoria e disco, rede, tempo de atividade, informacoes do sistema, firewall, monitor de portas, visualizador de logs, usuarios/permissoes, certificados e muito mais na maioria dos servidores baseados em Linux. Inclui graficos de historico em serie temporal e alertas baseados em limites com suporte a ntfy e webhook.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Autenticacao de Usuarios:**
|
||||
Gerenciamento seguro de usuarios com controles de administrador (podem editar informacoes de outros usuarios) e suporte para OIDC/LDAP/SSO (com controle de acesso), 2FA (TOTP) e passkey (WebAuthn). Visualize sessoes ativas de usuarios em todas as plataformas e revogue permissoes. Vincule suas contas OIDC/Locais entre si. Visualize o log de auditoria de todas as acoes dos usuarios.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Integracao com Tailscale:**
|
||||
Liste dispositivos da sua rede Tailscale para adicioná-los rapidamente como hosts, e conecte-se usando Tailscale SSH como metodo de autenticacao, deixando as ACLs da sua rede gerenciar a autorizacao sem armazenar credenciais.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Compartilhamento:**
|
||||
Crie funcoes e compartilhe hosts entre usuarios/funcoes. Suporta todos os tipos de autenticacao e todos os protocolos de host.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Conexoes Seriais:**
|
||||
Conecte-se a dispositivos seriais (roteadores, switches, microcontroladores, etc.) diretamente do navegador ou do aplicativo desktop. Configure taxa de baud, bits de dados, bits de parada e paridade. Usa a Web Serial API em navegadores suportados ou um backend nativo no aplicativo Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Alertas:**
|
||||
Defina regras de alerta baseadas em limites para metricas do host (CPU, memoria, disco, etc.) e receba notificacoes via ntfy ou webhooks quando forem ativadas. Visualize alertas ativos e resolvidos em um historico de registros.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Pagina Inicial:**
|
||||
Uma pagina inicial totalmente personalizavel com uma grade de widgets de arrastar e soltar. Adicione widgets para status do host, links de servicos, relogios, notas, feeds RSS, clima, conteineres Docker, graficos de metricas do host, terminais incorporados, iframes e mais.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Criptografia de Banco de Dados:**
|
||||
Backend armazenado como arquivos de banco de dados SQLite criptografados. Consulte a [documentacao](https://docs.termix.site/security) para mais informacoes.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Grafico de Rede:**
|
||||
Personalize seu Dashboard para visualizar seu homelab baseado nas suas conexoes SSH com suporte de status.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ferramentas SSH:**
|
||||
Crie trechos de comandos reutilizaveis que sao executados com um unico clique. Execute um comando simultaneamente em multiplos terminais abertos.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Abas Persistentes:**
|
||||
Sessoes SSH e abas permanecem abertas entre dispositivos/atualizacoes se habilitado no perfil do usuario.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Idiomas:**
|
||||
Suporte integrado para aproximadamente 30 idiomas (gerenciado pelo [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Compartilhamento de sessao:**
|
||||
Compartilhe uma sessao de terminal, RDP, VNC ou Telnet ao vivo com outras pessoas em tempo real. Compartilhe por meio de um link (entrada anonima, sem necessidade de conta) ou com um usuario especifico do Termix, e escolha acesso somente leitura ou leitura/gravacao. Os compartilhamentos podem expirar automaticamente ou ser revogados a qualquer momento, e o compartilhamento de sessao pode ser ativado globalmente ou por host.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Aplicativo de desktop autonomo + sincronizacao bidirecional:**
|
||||
O aplicativo de desktop Electron funciona de forma totalmente autonoma com seu proprio backend e banco de dados locais, sem necessidade de servidor. Opcionalmente, conecte-o a um servidor Termix remoto para sincronizacao bidirecional automatica de hosts, credenciais, snippets e muito mais, e escolha se as conexoes SSH sao iniciadas localmente ou por meio do servidor remoto.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Mais funcionalidades</b></summary>
|
||||
<br />
|
||||
|
||||
- **Dashboard** - Visualize informacoes do servidor de relance no seu dashboard
|
||||
- **Chaves de API** - Crie chaves de API com escopo de usuario e datas de expiracao para uso em automacao/CI
|
||||
- **Exportacao/Importacao de Dados** - Exporte e importe hosts SSH, credenciais e dados do gerenciador de arquivos
|
||||
- **Configuracao Automatica de SSL** - Geracao e gerenciamento integrado de certificados SSL com redirecionamentos HTTPS
|
||||
- **Interface Moderna** - Interface limpa compativel com desktop/mobile construida com React, Tailwind CSS e Shadcn. Escolha entre muitos temas de interface diferentes, incluindo claro, escuro, Dracula, etc. Use rotas de URL para abrir qualquer conexao em tela cheia.
|
||||
- **Historico de Comandos** - Autocompletar e visualizar comandos SSH executados anteriormente
|
||||
- **Conexao Rapida** - Conecte-se a um servidor sem precisar salvar os dados de conexao
|
||||
- **Paleta de Comandos** - Pressione duas vezes a tecla Shift esquerda para acessar rapidamente as conexoes SSH com seu teclado
|
||||
- **Integracao com Proxmox** - Adicione automaticamente hosts ao Termix a partir da sua instancia Proxmox
|
||||
- **SSH Rico em Funcionalidades** - Suporta jump hosts, Warpgate, conexoes baseadas em TOTP, SOCKS5, verificacao de chave do host, preenchimento automatico de senhas, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, registro de terminal, encaminhamento de agente SSH, agente SSH do Bitwarden, assinatura SSH do HashiCorp Vault, e mais.
|
||||
- **Termix ID** - Um equivalente ao sshid.io integrado ao Termix. Reivindique um identificador, publique suas chaves SSH publicas em uma URL de resolucao e use uma CA integrada para emitir certificados SSH.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Suporte a Plataformas
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Plataforma</th>
|
||||
<th align="center">Distribuicao</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Qualquer navegador moderno (Chrome, Safari, Firefox) · Suporte PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portatil · Instalador MSI · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portatil · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Instalacao
|
||||
|
||||
Visite a [documentacao](https://docs.termix.site/install) do Termix para instrucoes completas de instalacao em todas as plataformas.
|
||||
|
||||
Arquivo Docker Compose de exemplo (voce pode omitir o `guacd` e a rede se nao planeja usar recursos de area de trabalho remota):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Doar
|
||||
|
||||
Termix e gratuito e de codigo aberto, sem assinaturas ou planos pagos. Se o achar util, considere doar para ajudar a cobrir custos de servidor, dominios e tempo de desenvolvimento. As doacoes tambem ajudam a financiar o tempo de pesquisa e aprendizado necessario para construir funcionalidades como suporte a SAML, Kubernetes e Agent. Acompanhe o progresso e doe abaixo.
|
||||
|
||||
[Doar](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Patrocinadores
|
||||
|
||||
Interessado em um espaco pago para apoiar o desenvolvimento? Envie um email para [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Suporte
|
||||
|
||||
Se voce precisa de ajuda ou deseja solicitar uma funcionalidade para o Termix, visite a pagina de [Issues](https://github.com/Termix-SSH/Support/issues), faca login e clique em `New Issue`. Por favor, seja o mais detalhado possivel no seu relato, preferencialmente escrito em ingles. Voce tambem pode entrar no servidor do [Discord](https://discord.gg/jVQGdvHDrf) e visitar o canal de suporte, porem, os tempos de resposta podem ser mais longos.
|
||||
|
||||
<br />
|
||||
|
||||
## Capturas de Tela
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Assista resumos de atualizacoes no YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Alguns videos e imagens podem estar desatualizados ou podem nao mostrar perfeitamente as funcionalidades.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Funcionalidades Planejadas
|
||||
|
||||
Consulte [Projetos](https://github.com/orgs/Termix-SSH/projects/5) para todas as funcionalidades planejadas. Se voce deseja contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Licenca
|
||||
|
||||
Distribuido sob a Licenca Apache Versao 2.0. Consulte `LICENSE` para mais informacoes.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Самостоятельно размещаемое управление SSH и доступ к удалённому рабочему столу</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
Русский ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix — бесплатный проект с открытым исходным кодом. Если он вам полезен, рассмотрите возможность [пожертвования](https://donate.termix.site/) для покрытия расходов на серверы и время разработки.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Достигнуто 1 сентября 2025 года</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Обзор
|
||||
|
||||
Termix - это платформа для управления серверами с открытым исходным кодом, навсегда бесплатная и размещаемая на собственном сервере. Она предоставляет мультиплатформенное решение для управления вашими серверами и инфраструктурой через единый интуитивно понятный интерфейс. Termix предлагает доступ к SSH-терминалу, управление удаленным рабочим столом (RDP, VNC, Telnet), возможности SSH-туннелирования, удаленное управление файлами SSH и множество других инструментов. Termix - это идеальная бесплатная альтернатива Termius с возможностью размещения на собственном сервере, доступная для всех платформ.
|
||||
|
||||
<br />
|
||||
|
||||
## Возможности
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Доступ к SSH-терминалу:**
|
||||
Полнофункциональный терминал с поддержкой разделения экрана (до 4 панелей) и системой вкладок, как в браузере. Включает поддержку настройки терминала, включая популярные темы, шрифты и другие компоненты.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Доступ к удалённому рабочему столу:**
|
||||
Поддержка RDP, VNC и Telnet через браузер с полной настройкой и разделением экрана.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Управление SSH-туннелями:**
|
||||
Создание и управление межсерверными SSH-туннелями с автоматическим переподключением, мониторингом состояния и локальной, удалённой или динамической SOCKS-переадресацией. Настройки туннелей «десктопный клиент - сервер» хранятся локально для каждой установки; опциональные снимки C2S-пресетов можно сохранять на сервере, переименовывать, загружать или удалять, когда вы хотите перенести локальную конфигурацию туннеля между клиентами.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Удалённый файловый менеджер:**
|
||||
Управление файлами непосредственно на удалённых серверах с поддержкой просмотра и редактирования кода, изображений, аудио и видео. Загрузка, скачивание, переименование, удаление и перемещение файлов с поддержкой sudo. Включает поддержку перемещения файлов с сервера на сервер.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Управление Docker и Podman:**
|
||||
Запуск, остановка, приостановка, удаление контейнеров. Просмотр статистики контейнеров. Управление контейнером через терминал docker exec. Поддерживает как Docker, так и Podman в качестве среды выполнения контейнеров. Не предназначен для замены Portainer или Dockge, а скорее для простого управления контейнерами по сравнению с их созданием.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Менеджер SSH-хостов:**
|
||||
Сохранение, организация и управление SSH-подключениями с помощью тегов и папок (с настройкой папок и поддержкой вложенных папок), с возможностью сохранения данных для повторного входа и автоматизации развёртывания SSH-ключей.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Метрики хоста:**
|
||||
Просмотр использования CPU, памяти и диска, сети, времени работы, информации о системе, файрвола, монитора портов, просмотрщика логов, пользователей/прав доступа, сертификатов и многого другого на большинстве серверов на базе Linux. Включает графики истории временных рядов и оповещения на основе пороговых значений с поддержкой ntfy и вебхуков.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Аутентификация пользователей:**
|
||||
Безопасное управление пользователями с административным контролем (может редактировать информацию других пользователей) и поддержкой OIDC/LDAP/SSO (с контролем доступа), 2FA (TOTP) и поддержкой ключей доступа (WebAuthn). Просмотр активных сессий пользователей на всех платформах и отзыв прав доступа. Связывание аккаунтов OIDC/локальных аккаунтов. Просмотр журнала аудита действий всех пользователей.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Интеграция с Tailscale:**
|
||||
Список устройств вашей сети Tailscale для быстрого добавления их в качестве хостов и подключение через Tailscale SSH в качестве метода аутентификации, позволяя ACL вашей сети управлять авторизацией без хранения учётных данных.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Общий доступ:**
|
||||
Создание ролей и предоставление общего доступа к хостам для пользователей/ролей. Поддерживает все типы аутентификации и все протоколы хостов.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Последовательные подключения:**
|
||||
Подключение к последовательным устройствам (маршрутизаторы, коммутаторы, микроконтроллеры и т. д.) напрямую из браузера или приложения для рабочего стола. Настройка скорости передачи данных, битов данных, стоп-битов и чётности. Использует Web Serial API в поддерживаемых браузерах или нативный бэкенд в приложении Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Оповещения:**
|
||||
Настройте правила оповещений на основе пороговых значений для метрик хоста (CPU, память, диск и т. д.) и получайте уведомления через ntfy или вебхуки при их срабатывании. Просматривайте активные и разрешённые оповещения в журнале истории.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Домашняя страница:**
|
||||
Полностью настраиваемая домашняя страница с сеткой виджетов с перетаскиванием. Добавляйте виджеты для статуса хоста, ссылок на сервисы, часов, заметок, RSS-лент, погоды, контейнеров Docker, графиков метрик хоста, встроенных терминалов, iframe и многого другого.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Шифрование базы данных:**
|
||||
Бэкенд хранится в виде зашифрованных файлов базы данных SQLite. Подробнее в [документации](https://docs.termix.site/security).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Сетевой граф:**
|
||||
Настройте панель управления для визуализации вашей домашней лаборатории на основе SSH-подключений с поддержкой статусов.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Инструменты SSH:**
|
||||
Создание переиспользуемых фрагментов команд, выполняемых одним нажатием. Запуск одной команды одновременно в нескольких открытых терминалах.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Постоянные вкладки:**
|
||||
SSH-сессии и вкладки остаются открытыми на всех устройствах/при обновлении страницы, если включено в профиле пользователя.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Языки:**
|
||||
Встроенная поддержка около 30 языков (управляется через [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Общий доступ к сеансу:**
|
||||
Делитесь сеансом терминала, RDP, VNC или Telnet с другими в режиме реального времени. Делитесь по ссылке (анонимное присоединение, учетная запись не требуется) или с конкретным пользователем Termix, выбирая доступ только для чтения или для чтения и записи. Общий доступ может автоматически истекать или быть отозван в любое время, а общий доступ к сеансам можно включать глобально или для отдельного хоста.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Автономное настольное приложение + двусторонняя синхронизация:**
|
||||
Настольное приложение на Electron полностью автономно, с собственным локальным бэкендом и базой данных, сервер не требуется. При желании подключите его к удаленному серверу Termix для автоматической двусторонней синхронизации хостов, учетных данных, сниппетов и прочего, и выберите, запускаются ли SSH-соединения локально или через удаленный сервер.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Больше возможностей</b></summary>
|
||||
<br />
|
||||
|
||||
- **Панель управления** - Просмотр информации о сервере на панели управления одним взглядом
|
||||
- **API-ключи** - Создание API-ключей с областью видимости пользователя и сроками действия для использования в автоматизации/CI
|
||||
- **Экспорт/импорт данных** - Экспорт и импорт SSH-хостов, учётных данных и данных файлового менеджера
|
||||
- **Автоматическая настройка SSL** - Встроенная генерация и управление SSL-сертификатами с перенаправлением на HTTPS
|
||||
- **Современный интерфейс** - Чистый интерфейс для десктопа и мобильных устройств, построенный на React, Tailwind CSS и Shadcn. Выбор между множеством различных тем интерфейса, включая светлую, тёмную, Dracula и т. д. Использование URL-маршрутов для открытия любого подключения в полноэкранном режиме.
|
||||
- **История команд** - Автодополнение и просмотр ранее выполненных SSH-команд
|
||||
- **Быстрое подключение** - Подключение к серверу без необходимости сохранения данных подключения
|
||||
- **Командная палитра** - Двойное нажатие левого Shift для быстрого доступа к SSH-подключениям с клавиатуры
|
||||
- **Интеграция с Proxmox** - Автоматическое добавление хостов в Termix из вашего экземпляра Proxmox
|
||||
- **Богатый функционал SSH** - Поддержка jump-хостов, Warpgate, подключений на основе TOTP, SOCKS5, верификации ключей хоста, автозаполнения паролей, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, логирования терминала, переадресации SSH-агента, SSH-агента Bitwarden, подписи SSH через HashiCorp Vault и многого другого.
|
||||
- **Termix ID** - Аналог sshid.io, встроенный в Termix. Зарегистрируйте имя пользователя, опубликуйте свои публичные SSH-ключи по URL резолвера и используйте встроенный ЦС для выдачи SSH-сертификатов.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Поддержка платформ
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Платформа</th>
|
||||
<th align="center">Дистрибутив</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Любой современный браузер (Chrome, Safari, Firefox) · Поддержка PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Портативная версия · Установщик MSI · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Портативная версия · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Установка
|
||||
|
||||
Посетите [документацию](https://docs.termix.site/install) Termix для получения полных инструкций по установке на всех платформах.
|
||||
|
||||
Пример файла Docker Compose (вы можете опустить `guacd` и сеть, если не планируете использовать функции удаленного рабочего стола):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Пожертвование
|
||||
|
||||
Termix бесплатен и имеет открытый исходный код, без подписок или платных тарифов. Если он вам полезен, рассмотрите возможность пожертвования, чтобы помочь покрыть расходы на серверы, домены и время разработки. Пожертвования также помогают финансировать время на исследование и изучение того, что необходимо для создания таких функций, как поддержка SAML, Kubernetes и Agent. Отслеживайте прогресс и делайте пожертвования ниже.
|
||||
|
||||
[Пожертвовать](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Спонсоры
|
||||
|
||||
Заинтересованы в платном размещении для поддержки разработки? Напишите на [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Поддержка
|
||||
|
||||
Если вам нужна помощь или вы хотите запросить новую функцию для Termix, посетите страницу [Проблемы](https://github.com/Termix-SSH/Support/issues), войдите в систему и нажмите `New Issue`. Пожалуйста, опишите вашу проблему как можно подробнее, предпочтительно на английском языке. Вы также можете присоединиться к серверу [Discord](https://discord.gg/jVQGdvHDrf) и обратиться в канал поддержки, однако время ответа может быть дольше.
|
||||
|
||||
<br />
|
||||
|
||||
## Скриншоты
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Смотрите обзоры обновлений на YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Некоторые видео и изображения могут быть устаревшими или не полностью отражать функциональность.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Запланированные функции
|
||||
|
||||
Смотрите [Проекты](https://github.com/orgs/Termix-SSH/projects/5) для просмотра всех запланированных функций. Если вы хотите внести вклад, смотрите [Участие в разработке](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Лицензия
|
||||
|
||||
Распространяется по лицензии Apache License Version 2.0. Подробнее см. в файле `LICENSE`.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Kendi sunucunuzda barindirilan SSH yonetimi ve uzak masaustu erisimi</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
Türkçe ·
|
||||
<a href="README-VI.md">Tiếng Việt</a> ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix ücretsiz ve açık kaynaklıdır. Faydalı buluyorsanız, sunucu maliyetleri ve geliştirme süresine katkıda bulunmak için [bağış yapmayı](https://donate.termix.site/) düşünebilirsiniz.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>1 Eylül 2025'te kazanildi</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Genel Bakis
|
||||
|
||||
Termix, acik kaynakli, sonsuza kadar ucretsiz, kendi sunucunuzda barindirabileceginez hepsi bir arada sunucu yonetim platformudur. Sunucularinizi ve altyapinizi tek bir sezgisel arayuz uzerinden yonetmek icin cok platformlu bir cozum sunar. Termix, SSH terminal erisimi, uzak masaustu kontrolu (RDP, VNC, Telnet), SSH tunelleme yetenekleri, uzak dosya yonetimi ve daha bircok arac saglar. Termix, tum platformlarda kullanilabilen Termius'un mukemmel ucretsiz ve kendi barindirmali alternatifidir.
|
||||
|
||||
<br />
|
||||
|
||||
## Ozellikler
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Terminal Erisimi:**
|
||||
Tarayici benzeri sekme sistemiyle bolunmus ekran destegine sahip (4 panele kadar) tam ozellikli terminal. Yaygin terminal temalari, yazi tipleri ve diger bilesenleri iceren terminal ozellestirme destegi.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Uzak Masaustu Erisimi:**
|
||||
Tam ozellestirme ve bolunmus ekran ile tarayici uzerinden RDP, VNC ve Telnet destegi.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Tunel Yonetimi:**
|
||||
Otomatik yeniden baglanti, saglik izleme ve yerel, uzak veya dinamik SOCKS yonlendirme ile sunucular arasi SSH tunelleri olusturun ve yonetin. Masaustu istemci-sunucu tunel ayarlari her masaustu kurulumu icin yerel olarak depolanir; istege bagli C2S hazir ayar anlik goruntuleri, yerel bir tunel yapilandirmasini istemciler arasinda tasimak istediginizde sunucuya kaydedilebilir, yeniden adlandirilabilir, yuklenebilir veya silinebilir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Uzak Dosya Yoneticisi:**
|
||||
Uzak sunuculardaki dosyalari dogrudan yonetin; kod, goruntu, ses ve video goruntuleme ve duzenleme destegi ile. Sudo destegi ile dosyalari sorunsuzca yukleyin, indirin, yeniden adlandirin, silin ve tasiyin. Dosyalari sunucudan sunucuya tasima destegini de icerir.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Docker ve Podman Yonetimi:**
|
||||
Konteynerleri baslatın, durdurun, duraklatın, kaldirin. Konteyner istatistiklerini goruntuleyin. Docker exec terminali kullanarak konteyneri kontrol edin. Docker ve Podman'i konteyner calisma ortami olarak destekler. Portainer veya Dockge'nin yerini almak icin degil, konteynerlerinizi olusturmak yerine basitce yonetmek icin tasarlanmistir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Ana Bilgisayar Yoneticisi:**
|
||||
SSH baglantilarinizi etiketler ve klasorlerle (klasor ozellestirme ve ic ice klasor destegi ile) kaydedin, duzenleyin ve yonetin; yeniden kullanilabilir giris bilgilerini kolayca kaydedin ve SSH anahtarlarinin dagitimini otomatiklestirin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ana Bilgisayar Metrikleri:**
|
||||
Cogu Linux tabanli sunucularda calisan CPU, bellek, disk kullanimi, ag, calisma suresi, sistem bilgisi, guvenlik duvari, port izleme, gunluk goruntuleyici, kullanicilar/izinler, sertifikalar ve daha fazlasini goruntuleyin. Zaman serisi gecmis grafiklerini ve ntfy ile webhook destekli esik tabanli uyarilari icerir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Kullanici Kimlik Dogrulama:**
|
||||
Yonetici kontrolleri (diger kullanicilarin bilgilerini duzenleyebilir), OIDC/LDAP/SSO (erisim kontrollu), 2FA (TOTP) ve passkey (WebAuthn) destegi ile guvenli kullanici yonetimi. Tum platformlardaki aktif kullanici oturumlarini goruntuleyin ve izinleri iptal edin. OIDC/Yerel hesaplarinizi birbirine baglayin. Tum kullanicilarin islemlerinin denetim gunlugunu goruntuleyin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tailscale Entegrasyonu:**
|
||||
Tailscale aginizdaki cihazlari listeleyerek hizlica ana bilgisayar olarak ekleyin ve kimlik dogrulama yontemi olarak Tailscale SSH kullanarak baglanin; bu sayede ag ACL'leriniz kimlik bilgileri depolamadan yetkilendirmeyi yonetir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Paylasim:**
|
||||
Roller olusturun ve ana bilgisayarlari kullanicilar/roller arasinda paylasin. Tum kimlik dogrulama turlerini ve tum ana bilgisayar protokollerini destekler.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Seri Baglantilar:**
|
||||
Seri cihazlara (router, switch, mikrodenetleyici vb.) dogrudan tarayici veya masaustu uygulamasindan baglanin. Baud hizi, veri bitleri, durdurma bitleri ve parite yapilandirin. Desteklenen tarayicilarda Web Serial API, Electron uygulamasinda yerel arka ucu kullanir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Uyarilar:**
|
||||
Ana bilgisayar metrikleri (CPU, bellek, disk vb.) icin esik tabanli uyari kurallari belirleyin ve tetiklendiklerinde ntfy veya webhook araciligiyla bildirim alin. Gecmis gunlugunde tetiklenen ve cozulen uyarilari goruntuleyin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ana Sayfa:**
|
||||
Surukleme ve birakma widget izgarasina sahip tamamen ozellestirilebilir bir ana sayfa. Ana bilgisayar durumu, hizmet baglantilari, saatler, notlar, RSS besleme, hava durumu, Docker konteynerleri, ana bilgisayar metrik grafikleri, gomulu terminaller, iframe ve daha fazlasi icin widget ekleyin.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Veritabani Sifreleme:**
|
||||
Arka uc, sifrelenmis SQLite veritabani dosyalari olarak depolanir. Daha fazla bilgi icin [belgelere](https://docs.termix.site/security) bakin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ag Grafigi:**
|
||||
Kontrol panelinizi, SSH baglantilariniza dayali olarak ev laboratuvarinizi durum destegi ile gorselletirmek icin ozellestirin.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**SSH Araclari:**
|
||||
Tek tiklamayla calistirilan yeniden kullanilabilir komut parcaciklari olusturun. Birden fazla acik terminalde ayni anda tek bir komut calistirin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Kalici Sekmeler:**
|
||||
Kullanici profilinde etkinlestirilmisse SSH oturumlari ve sekmeler cihazlar/yenilemeler arasinda acik kalir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Diller:**
|
||||
Yaklasik 30 dil icin yerlesik destek ([Crowdin](https://docs.termix.site/translations) tarafindan yonetilir).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Oturum Paylasimi:**
|
||||
Canli bir terminal, RDP, VNC veya Telnet oturumunu baskalariyla gercek zamanli olarak paylasin. Bir baglanti uzerinden (anonim olarak katilir, hesap gerekmez) veya belirli bir Termix kullanicisiyla paylasin ve salt okunur veya okuma/yazma erisimi secin. Paylasimlar otomatik olarak sona erebilir veya istediginiz zaman iptal edilebilir; oturum paylasimi genel olarak veya sunucu bazinda acilip kapatilabilir.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Bagimsiz Masaustu + Cift Yonlu Senkronizasyon:**
|
||||
Electron masaustu uygulamasi, kendi yerel arka ucu ve veritabaniyla tamamen bagimsiz calisir, sunucu gerekmez. Istege bagli olarak sunucular, kimlik bilgileri, kod parcaciklari ve daha fazlasinin otomatik cift yonlu senkronizasyonu icin uzak bir Termix sunucusuna baglayin ve SSH baglantilarinin yerel olarak mi yoksa uzak sunucu uzerinden mi baslatilacagini secin.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Daha fazla ozellik</b></summary>
|
||||
<br />
|
||||
|
||||
- **Kontrol Paneli** - Kontrol panelinizde sunucu bilgilerini bir bakista goruntuleyin
|
||||
- **API Anahtarlari** - Otomasyon/CI icin kullanilmak uzere son kullanma tarihleriyle kullanici kapsamli API anahtarlari olusturun
|
||||
- **Veri Disa/Ice Aktarma** - SSH ana bilgisayarlarini, kimlik bilgilerini ve dosya yoneticisi verilerini disa ve ice aktarin
|
||||
- **Otomatik SSL Kurulumu** - HTTPS yonlendirmeleriyle yerlesik SSL sertifika olusturma ve yonetimi
|
||||
- **Modern Arayuz** - React, Tailwind CSS ve Shadcn ile olusturulmus temiz masaustu/mobil uyumlu arayuz. Isik, karanlik, Dracula vb. dahil olmak uzere bircok farkli UI temasi arasından secim yapin. Herhangi bir baglantıyı tam ekranda acmak icin URL yollarini kullanin.
|
||||
- **Komut Gecmisi** - Daha once calistirilan SSH komutlarini otomatik tamamlayin ve goruntuleyin
|
||||
- **Hizli Baglanti** - Baglanti verilerini kaydetmeden bir sunucuya baglanin
|
||||
- **Komut Paleti** - Sol shift tusuna iki kez basarak SSH baglantilariniza klavyenizle hizlica erisin
|
||||
- **Proxmox Entegrasyonu** - Proxmox ornekinizden Termix'e otomatik olarak ana bilgisayar ekleyin
|
||||
- **SSH Zengin Ozellikler** - Atlama ana bilgisayarlari, Warpgate, TOTP tabanli baglantilar, SOCKS5, ana bilgisayar anahtar dogrulama, otomatik sifre doldurma, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal gunlukleme, SSH agent forwarding, Bitwarden SSH agent, HashiCorp Vault SSH imzalama ve dahasini destekler.
|
||||
- **Termix ID** - Termix'e entegre edilmis bir sshid.io esdegeri. Bir kullanici adi edinin, genel SSH anahtarlarinizi bir cozumleyici URL'sinde yayinlayin ve SSH sertifikalari vermek icin yerlesik bir CA kullanin.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Platform Destegi
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Platform</th>
|
||||
<th align="center">Dagitim</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Herhangi bir modern tarayici (Chrome, Safari, Firefox) · PWA destegi</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Tasınabilir · MSI Yukleyici · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Tasınabilir · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Kurulum
|
||||
|
||||
Termix'i tum platformlara nasil kuracaginiz hakkinda daha fazla bilgi icin Termix [Belgelerine](https://docs.termix.site/install) bakin.
|
||||
|
||||
Ornek bir Docker Compose dosyasi (uzak masaustu ozelliklerini kullanmayi planlamiyorsaniz `guacd` ve agi cikarabilirsiniz):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Bağış Yapın
|
||||
|
||||
Termix ücretsiz ve açık kaynaklıdır, abonelik veya ücretli plan yoktur. Faydalı buluyorsaniz, sunucu maliyetleri, alan adlari ve gelistirme suresine katkida bulunmak icin bagis yapmayi dusunebilirsiniz. Bagislar ayrica SAML, Kubernetes ve Agent destegi gibi ozellikleri gelistirmek icin gereken arastirma ve ogrenme suresini finanse etmeye yardimci olur. Ilerlemeyi takip edin ve asagidan bagis yapin.
|
||||
|
||||
[Bağış Yapın](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Sponsorlar
|
||||
|
||||
Gelistirmeyi desteklemek icin ucretli bir yerlesim ile ilgileniyor musunuz? [mail@termix.site](mailto:mail@termix.site) adresine e-posta gonderin.
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Destek
|
||||
|
||||
Termix ile ilgili yardima ihtiyaciniz varsa veya bir ozellik talep etmek istiyorsaniz, [Sorunlar](https://github.com/Termix-SSH/Support/issues) sayfasini ziyaret edin, giris yapin ve `New Issue` butonuna basin. Lutfen sorununuzu mumkun oldugunca ayrintili yazin, tercihen Ingilizce olarak. Ayrica [Discord](https://discord.gg/jVQGdvHDrf) sunucusuna katilabilir ve destek kanalini ziyaret edebilirsiniz, ancak yanit sureleri daha uzun olabilir.
|
||||
|
||||
<br />
|
||||
|
||||
## Ekran Goruntuleri
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>YouTube'da guncelleme ozetlerini izleyin</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Bazi videolar ve gorseller guncel olmayabilir veya ozellikleri tam olarak yansitmayabilir.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Planlanan Ozellikler
|
||||
|
||||
Tum planlanan ozellikler icin [Projeler](https://github.com/orgs/Termix-SSH/projects/5) sayfasina bakin. Katkida bulunmak istiyorsaniz, [Katkida Bulunma](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) sayfasina bakin.
|
||||
|
||||
<br />
|
||||
|
||||
## Lisans
|
||||
|
||||
Apache Lisansi Surumu 2.0 altinda dagitilmaktadir. Daha fazla bilgi icin `LICENSE` dosyasina bakin.
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="../public/icon.svg" width="120" height="120" alt="Termix Logo" />
|
||||
|
||||
<h1>Termix</h1>
|
||||
|
||||
<p>Quan ly SSH tu luu tru va truy cap may tinh tu xa</p>
|
||||
|
||||
<p>
|
||||
<a href="../README.md">English</a> ·
|
||||
<a href="README-CN.md">中文</a> ·
|
||||
<a href="README-JA.md">日本語</a> ·
|
||||
<a href="README-KO.md">한국어</a> ·
|
||||
<a href="README-FR.md">Français</a> ·
|
||||
<a href="README-DE.md">Deutsch</a> ·
|
||||
<a href="README-ES.md">Español</a> ·
|
||||
<a href="README-PT.md">Português</a> ·
|
||||
<a href="README-RU.md">Русский</a> ·
|
||||
<a href="README-AR.md">العربية</a> ·
|
||||
<a href="README-HI.md">हिन्दी</a> ·
|
||||
<a href="README-TR.md">Türkçe</a> ·
|
||||
Tiếng Việt ·
|
||||
<a href="README-IT.md">Italiano</a>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<br />
|
||||
|
||||
Termix là dự án miễn phí và mã nguồn mở. Nếu bạn thấy hữu ích, hãy cân nhắc [quyên góp](https://donate.termix.site/) để giúp trang trải chi phí máy chủ và thời gian phát triển.
|
||||
|
||||
<br />
|
||||
|
||||
<img src="../repo-images/Termix Header.png" alt="Termix Banner" width="900" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<p>
|
||||
<img src="../repo-images/Repo of the Day.png" alt="Repo of the Day Achievement" width="280" />
|
||||
<br />
|
||||
<sub>Dat duoc vao ngay 1 thang 9 nam 2025</sub>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Tong Quan
|
||||
|
||||
Termix la nen tang quan ly may chu tat ca trong mot, ma nguon mo, mien phi vinh vien, tu luu tru. No cung cap giai phap da nen tang de quan ly may chu va co so ha tang cua ban thong qua mot giao dien truc quan duy nhat. Termix cung cap quyen truy cap terminal SSH, dieu khien may tinh tu xa (RDP, VNC, Telnet), kha nang tao duong ham SSH, quan ly tep tu xa va nhieu cong cu khac. Termix la giai phap thay the mien phi va tu luu tru hoan hao cho Termius, kha dung tren tat ca cac nen tang.
|
||||
|
||||
<br />
|
||||
|
||||
## Tinh Nang
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Truy Cap Terminal SSH:**
|
||||
Terminal day du tinh nang voi ho tro chia man hinh (len den 4 bang) voi he thong tab kieu trinh duyet. Bao gom ho tro tuy chinh terminal bao gom cac chu de terminal pho bien, phong chu va cac thanh phan khac.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Truy Cap Man Hinh Tu Xa:**
|
||||
Ho tro RDP, VNC va Telnet qua trinh duyet voi day du tuy chinh va chia man hinh.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Quan Ly Duong Ham SSH:**
|
||||
Tao va quan ly duong ham SSH giua cac may chu voi tu dong ket noi lai, giam sat suc khoe va chuyen tiep cuc bo, tu xa hoac SOCKS dong. Cai dat duong ham tu may khach desktop den may chu duoc luu tru cuc bo cho moi ban cai dat desktop; cac snapshot C2S preset tuy chon co the duoc luu tren may chu, doi ten, tai hoac xoa khi ban muon di chuyen mot cau hinh duong ham cuc bo giua cac may khach.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Trinh Quan Ly Tep Tu Xa:**
|
||||
Quan ly tep truc tiep tren may chu tu xa voi ho tro xem va chinh sua ma, hinh anh, am thanh va video. Tai len, tai xuong, doi ten, xoa va di chuyen tep lien mach voi ho tro sudo. Bao gom ho tro di chuyen tep tu may chu nay sang may chu khac.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Quan Ly Docker va Podman:**
|
||||
Khoi dong, dung, tam dung, xoa container. Xem thong ke container. Dieu khien container bang terminal docker exec. Ho tro ca Docker va Podman lam moi truong chay container. Khong duoc tao ra de thay the Portainer hay Dockge ma don gian la de quan ly container cua ban thay vi tao moi chung.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Trinh Quan Ly May Chu SSH:**
|
||||
Luu, sap xep va quan ly cac ket noi SSH cua ban voi the va thu muc (ho tro tuy chinh thu muc va thu muc long nhau), de dang luu thong tin dang nhap co the tai su dung dong thoi co the tu dong hoa viec trien khai khoa SSH.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Chi So May Chu:**
|
||||
Xem muc su dung CPU, bo nho, o dia, mang, thoi gian hoat dong, thong tin he thong, tuong lua, giam sat cong, trinh xem nhat ky, nguoi dung/quyen, chung chi va nhieu hon nua tren hau het cac may chu chay Linux. Bao gom bieu do lich su theo chuoi thoi gian va canh bao dua tren nguong voi ho tro ntfy va webhook.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Xac Thuc Nguoi Dung:**
|
||||
Quan ly nguoi dung an toan voi quyen quan tri (co the chinh sua thong tin cua nguoi dung khac) va ho tro OIDC/LDAP/SSO (co kiem soat truy cap), 2FA (TOTP) va passkey (WebAuthn). Xem phien hoat dong cua nguoi dung tren tat ca cac nen tang va thu hoi quyen. Lien ket tai khoan OIDC/Noi bo cua ban voi nhau. Xem nhat ky kiem toan cac hanh dong cua tat ca nguoi dung.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tich Hop Tailscale:**
|
||||
Liet ke cac thiet bi trong mang Tailscale de nhanh chong them vao lam may chu, va ket noi bang Tailscale SSH lam phuong thuc xac thuc, de cac ACL mang xu ly uy quyen ma khong can luu tru thong tin xac thuc.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**RBAC/Chia Se:**
|
||||
Tao vai tro va chia se may chu giua nguoi dung/vai tro. Ho tro tat ca cac loai xac thuc va tat ca cac giao thuc may chu.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ket Noi Noi Tiep:**
|
||||
Ket noi voi cac thiet bi noi tiep (router, switch, vi dieu khien, v.v.) truc tiep tu trinh duyet hoac ung dung may tinh. Cau hinh toc do baud, bit du lieu, bit dung va chan le. Su dung Web Serial API tren trinh duyet duoc ho tro hoac backend ban dia trong ung dung Electron.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Canh Bao:**
|
||||
Dat cac quy tac canh bao dua tren nguong cho chi so may chu (CPU, bo nho, o dia, v.v.) va nhan thong bao qua ntfy hoac webhook khi chung kich hoat. Xem canh bao dang kich hoat va da giai quyet trong nhat ky lich su.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Trang Chu:**
|
||||
Trang chu co the tuy chinh hoan toan voi luoi widget keo va tha. Them widget cho trang thai may chu, lien ket dich vu, dong ho, ghi chu, feed RSS, thoi tiet, container Docker, bieu do chi so may chu, terminal nhung, iframe va nhieu hon nua.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ma Hoa Co So Du Lieu:**
|
||||
Backend duoc luu tru duoi dang tep co so du lieu SQLite duoc ma hoa. Xem [tai lieu](https://docs.termix.site/security) de biet them.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Bieu Do Mang:**
|
||||
Tuy chinh Bang Dieu Khien de truc quan hoa homelab cua ban dua tren cac ket noi SSH voi ho tro trang thai.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Cong Cu SSH:**
|
||||
Tao doan lenh co the tai su dung, thuc thi chi voi mot cu nhap chuot. Chay mot lenh dong thoi tren nhieu terminal dang mo.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Tab Lien Tuc:**
|
||||
Cac phien SSH va tab van mo tren cac thiet bi/lan lam moi neu duoc bat trong ho so nguoi dung.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ngon Ngu:**
|
||||
Ho tro tich hop khoang 30 ngon ngu (duoc quan ly boi [Crowdin](https://docs.termix.site/translations)).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Chia Se Phien:**
|
||||
Chia se mot phien terminal, RDP, VNC, hoac Telnet truc tiep voi nguoi khac theo thoi gian thuc. Chia se qua lien ket (tham gia an danh, khong can tai khoan) hoac voi mot nguoi dung Termix cu the, va chon quyen truy cap chi doc hoac doc/ghi. Cac lien ket chia se co the tu dong het han hoac bi thu hoi bat cu luc nao, va tinh nang chia se phien co the duoc bat/tat toan cuc hoac theo tung host.
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
**Ung Dung Desktop Doc Lap + Dong Bo 2 Chieu:**
|
||||
Ung dung desktop Electron chay hoan toan doc lap voi backend va co so du lieu cuc bo rieng, khong can may chu. Tuy chon ket noi voi may chu Termix tu xa de tu dong dong bo 2 chieu cac host, thong tin dang nhap, doan ma va nhieu hon nua, va chon xem cac ket noi SSH duoc khoi tao cuc bo hay thong qua may chu tu xa.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary><b>Them tinh nang</b></summary>
|
||||
<br />
|
||||
|
||||
- **Bang Dieu Khien** - Xem thong tin may chu trong nháy mat tren bang dieu khien cua ban
|
||||
- **Khoa API** - Tao khoa API theo pham vi nguoi dung voi ngay het han de su dung cho tu dong hoa/CI
|
||||
- **Xuat/Nhap Du Lieu** - Xuat va nhap may chu SSH, thong tin xac thuc va du lieu trinh quan ly tep
|
||||
- **Thiet Lap SSL Tu Dong** - Tao va quan ly chung chi SSL tich hop voi chuyen huong HTTPS
|
||||
- **Giao Dien Hien Dai** - Giao dien sach se, than thien voi may tinh/di dong duoc xay dung bang React, Tailwind CSS va Shadcn. Chon giua nhieu chu de UI khac nhau bao gom sang, toi, Dracula, v.v. Su dung duong dan URL de mo bat ky ket noi nao o che do toan man hinh.
|
||||
- **Lich Su Lenh** - Tu dong hoan thanh va xem cac lenh SSH da chay truoc do
|
||||
- **Ket Noi Nhanh** - Ket noi den may chu ma khong can luu du lieu ket noi
|
||||
- **Bang Lenh** - Nhan dup phim shift trai de truy cap nhanh cac ket noi SSH bang ban phim
|
||||
- **Tich Hop Proxmox** - Tu dong them may chu vao Termix tu instance Proxmox cua ban
|
||||
- **SSH Giau Tinh Nang** - Ho tro jump host, Warpgate, ket noi dua tren TOTP, SOCKS5, xac minh khoa may chu, tu dong dien mat khau, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, ghi nhat ky terminal, chuyen tiep SSH agent, Bitwarden SSH agent, ky SSH bang HashiCorp Vault va nhieu hon nua.
|
||||
- **Termix ID** - Mot tuong duong cua sshid.io duoc tich hop san trong Termix. Dang ky mot ten dinh danh, cong bo khoa SSH cong khai cua ban tai mot URL phan giai va su dung CA tich hop san de cap chung chi SSH.
|
||||
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
## Ho Tro Nen Tang
|
||||
|
||||
<table align="center">
|
||||
<tr>
|
||||
<th align="center">Nen tang</th>
|
||||
<th align="center">Phan phoi</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Web</b></td>
|
||||
<td>Bat ky trinh duyet hien dai nao (Chrome, Safari, Firefox) · Ho tro PWA</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Windows</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · MSI Installer · Chocolatey</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>Linux</b> <sub>x64/ia32</sub></td>
|
||||
<td>Portable · AUR · AppImage · Deb · Flatpak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><b>macOS</b> <sub>x64/ia32, 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 />
|
||||
|
||||
## Cai Dat
|
||||
|
||||
Truy cap [Tai Lieu](https://docs.termix.site/install) Termix de biet them thong tin ve cach cai dat Termix tren tat ca cac nen tang.
|
||||
|
||||
Tep Docker Compose mau (ban co the bo qua `guacd` va mang neu khong co y dinh su dung cac tinh nang dieu khien may tinh tu xa):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:latest
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- termix-data:/app/data
|
||||
environment:
|
||||
PORT: "8080"
|
||||
depends_on:
|
||||
- guacd
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
guacd:
|
||||
image: guacamole/guacd:1.6.0
|
||||
container_name: guacd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4822:4822"
|
||||
networks:
|
||||
- termix-net
|
||||
|
||||
volumes:
|
||||
termix-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
termix-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
## Quyên góp
|
||||
|
||||
Termix là dự án miễn phí và mã nguồn mở, không có gói đăng ký hay trả phí. Nếu bạn thấy hữu ích, hãy cân nhắc quyên góp để giúp trang trải chi phí máy chủ, tên miền và thời gian phát triển. Các khoản quyên góp cũng giúp tài trợ thời gian nghiên cứu và tìm hiểu những gì cần thiết để xây dựng các tính năng như SAML, Kubernetes và hỗ trợ Agent. Theo dõi tiến độ và quyên góp bên dưới.
|
||||
|
||||
[Quyên góp](https://donate.termix.site/)
|
||||
|
||||
<br />
|
||||
|
||||
## Nha Tai Tro
|
||||
|
||||
Ban quan tam den viec dat quang cao tra phi de ho tro phat trien? Gui email toi [mail@termix.site](mailto:mail@termix.site).
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
<a href="https://www.digitalocean.com/">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="40" alt="DigitalOcean" />
|
||||
</a>
|
||||
|
||||
<a href="https://crowdin.com/">
|
||||
<img src="https://support.crowdin.com/assets/logos/core-logo/svg/crowdin-core-logo-cDark.svg" height="40" alt="Crowdin" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.blacksmith.sh/">
|
||||
<img src="https://cdn.prod.website-files.com/681bfb0c9a4601bc6e288ec4/683ca9e2c5186757092611b8_e8cb22127df4da0811c4120a523722d2_logo-backsmith-wordmark-light.svg" height="40" alt="Blacksmith" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.cloudflare.com/">
|
||||
<img src="https://sirv.sirv.com/website/screenshots/cloudflare/cloudflare-logo.png?w=300" height="40" alt="Cloudflare" />
|
||||
</a>
|
||||
|
||||
<a href="https://tailscale.com/">
|
||||
<img src="https://drive.google.com/uc?export=view&id=1lIxkJuX6M23bW-2FElhT0rQieTrzaVSL" height="40" alt="Tailscale" />
|
||||
</a>
|
||||
|
||||
<a href="https://akamai.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Akamai_logo.svg" height="40" alt="Akamai" />
|
||||
</a>
|
||||
|
||||
<a href="https://aws.amazon.com/">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Amazon_Web_Services_Logo.svg/960px-Amazon_Web_Services_Logo.svg.png" height="40" alt="AWS" />
|
||||
</a>
|
||||
|
||||
<a href="https://rackgenius.com/">
|
||||
<img src="https://rackgenius.com/rackgenius-logo.png" height="40" alt="Rack Genius" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Ho Tro
|
||||
|
||||
Neu ban can tro giup hoac muon yeu cau tinh nang voi Termix, hay truy cap trang [Van De](https://github.com/Termix-SSH/Support/issues), dang nhap va nhan `New Issue`. Vui long mo ta van de cang chi tiet cang tot, uu tien viet bang tieng Anh. Ban cung co the tham gia may chu [Discord](https://discord.gg/jVQGdvHDrf) va truy cap kenh ho tro, tuy nhien thoi gian phan hoi co the lau hon.
|
||||
|
||||
<br />
|
||||
|
||||
## Anh Chup Man Hinh
|
||||
|
||||
<div align="center">
|
||||
|
||||
<br />
|
||||
|
||||
[](https://www.youtube.com/@TermixSSH/videos)
|
||||
|
||||
<sub>Xem tong quan cap nhat tren YouTube</sub>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 1.png" alt="Termix Screenshot 1" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 2.png" alt="Termix Screenshot 2" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 3.png" alt="Termix Screenshot 3" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 4.png" alt="Termix Screenshot 4" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 5.png" alt="Termix Screenshot 5" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 6.png" alt="Termix Screenshot 6" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 7.png" alt="Termix Screenshot 7" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 8.png" alt="Termix Screenshot 8" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 9.png" alt="Termix Screenshot 9" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 10.png" alt="Termix Screenshot 10" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 11.png" alt="Termix Screenshot 11" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 12.png" alt="Termix Screenshot 12" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 13.png" alt="Termix Screenshot 13" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 14.png" alt="Termix Screenshot 14" width="400" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="../repo-images/Image 15.png" alt="Termix Screenshot 15" width="400" /></td>
|
||||
<td><img src="../repo-images/Image 16.png" alt="Termix Screenshot 16" width="400" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<sub>Mot so video va hinh anh co the da loi thoi hoac khong the hien chinh xac hoan toan cac tinh nang.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Tinh Nang Du Kien
|
||||
|
||||
Xem [Du An](https://github.com/orgs/Termix-SSH/projects/5) de biet tat ca cac tinh nang du kien. Neu ban muon dong gop, xem [Dong Gop](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
|
||||
|
||||
<br />
|
||||
|
||||
## Giay Phep
|
||||
|
||||
Duoc phan phoi theo Giay Phep Apache Phien Ban 2.0. Xem `LICENSE` de biet them thong tin.
|
||||
|
After Width: | Height: | Size: 364 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 527 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 794 KiB |
|
After Width: | Height: | Size: 567 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 404 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 449 KiB |
|
After Width: | Height: | Size: 534 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 493 KiB |
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "mysql",
|
||||
schema: "./src/backend/database/db/schema.mysql.ts",
|
||||
out: "./drizzle/mysql",
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "postgresql",
|
||||
schema: "./src/backend/database/db/schema.pg.ts",
|
||||
out: "./drizzle/postgres",
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "sqlite",
|
||||
schema: "./src/backend/database/db/schema.ts",
|
||||
out: "./drizzle/sqlite",
|
||||
});
|
||||
@@ -0,0 +1,890 @@
|
||||
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;
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "mysql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "5",
|
||||
"when": 1785738871436,
|
||||
"tag": "0000_clean_pretty_boy",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,837 @@
|
||||
CREATE TABLE "alert_firings" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"rule_id" integer NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"host_name" text NOT NULL,
|
||||
"fired_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"resolved_at" text,
|
||||
"value" double precision,
|
||||
"message" text NOT NULL,
|
||||
"severity" text DEFAULT 'warning' NOT NULL,
|
||||
"acknowledged" boolean DEFAULT false NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "alert_rule_channels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"rule_id" integer NOT NULL,
|
||||
"channel_id" integer NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "alert_rules" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"trigger_type" text NOT NULL,
|
||||
"threshold_value" double precision,
|
||||
"threshold_duration_seconds" integer,
|
||||
"cooldown_minutes" integer DEFAULT 15 NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "api_keys" (
|
||||
"id" varchar(255) PRIMARY KEY 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 DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text,
|
||||
"last_used_at" text,
|
||||
"is_active" boolean DEFAULT true NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "audit_logs" (
|
||||
"id" serial PRIMARY KEY 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 DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "c2s_tunnel_presets" (
|
||||
"id" serial PRIMARY KEY 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 DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "command_history" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"command" text NOT NULL,
|
||||
"executed_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "dashboard_service_links" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"label" text NOT NULL,
|
||||
"url" text NOT NULL,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "dashboard_service_links_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "dismissed_alerts" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"alert_id" text NOT NULL,
|
||||
"dismissed_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "file_manager_pinned" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"pinned_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "file_manager_recent" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"last_opened" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "file_manager_shortcuts" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "homepage_items" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"type_id" text NOT NULL,
|
||||
"title" text,
|
||||
"config" text DEFAULT '{}' NOT NULL,
|
||||
"folder_id" integer,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "homepage_items_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "homepage_layouts" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"layout" text DEFAULT '{}' NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "homepage_layouts_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "host_access" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"user_id" varchar(255),
|
||||
"role_id" integer,
|
||||
"granted_by" varchar(255) NOT NULL,
|
||||
"permission_level" text DEFAULT 'connect' NOT NULL,
|
||||
"expires_at" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"last_accessed_at" text,
|
||||
"access_count" integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "host_health_checks" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"checks" text NOT NULL,
|
||||
"interval_seconds" integer DEFAULT 300 NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "host_health_history" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"check_id" text NOT NULL,
|
||||
"ts" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"ok" boolean NOT NULL,
|
||||
"latency_ms" integer,
|
||||
"detail" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "host_metrics_history" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"ts" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"cpu_percent" double precision,
|
||||
"mem_percent" double precision,
|
||||
"disk_percent" double precision,
|
||||
"net_rx_bytes" integer,
|
||||
"net_tx_bytes" integer
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "host_metrics_preferences" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"layout" text NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "ssh_data" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"connection_type" text DEFAULT 'ssh' NOT NULL,
|
||||
"name" varchar(255),
|
||||
"ip" text NOT NULL,
|
||||
"port" integer NOT NULL,
|
||||
"username" text NOT NULL,
|
||||
"folder" text,
|
||||
"tags" text,
|
||||
"pin" boolean DEFAULT false NOT NULL,
|
||||
"auth_type" text NOT NULL,
|
||||
"use_warpgate" boolean DEFAULT false NOT NULL,
|
||||
"share_ssh_auth" boolean DEFAULT false NOT NULL,
|
||||
"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" integer,
|
||||
"override_credential_username" boolean,
|
||||
"vault_profile_id" integer,
|
||||
"enable_terminal" boolean DEFAULT true NOT NULL,
|
||||
"enable_session_logging" boolean DEFAULT true NOT NULL,
|
||||
"allow_session_sharing" boolean DEFAULT true NOT NULL,
|
||||
"enable_command_history" boolean DEFAULT true NOT NULL,
|
||||
"enable_tunnel" boolean DEFAULT true NOT NULL,
|
||||
"tunnel_connections" text,
|
||||
"jump_hosts" text,
|
||||
"enable_file_manager" boolean DEFAULT true NOT NULL,
|
||||
"scp_legacy" boolean DEFAULT false NOT NULL,
|
||||
"enable_docker" boolean DEFAULT false NOT NULL,
|
||||
"enable_tmux_monitor" boolean DEFAULT false NOT NULL,
|
||||
"show_terminal_in_sidebar" boolean DEFAULT true NOT NULL,
|
||||
"show_file_manager_in_sidebar" boolean DEFAULT false NOT NULL,
|
||||
"show_tunnel_in_sidebar" boolean DEFAULT false NOT NULL,
|
||||
"show_docker_in_sidebar" boolean DEFAULT false NOT NULL,
|
||||
"show_server_stats_in_sidebar" boolean DEFAULT false NOT NULL,
|
||||
"default_path" text,
|
||||
"stats_config" text,
|
||||
"docker_config" text,
|
||||
"enable_proxmox" boolean DEFAULT false NOT NULL,
|
||||
"proxmox_config" text,
|
||||
"terminal_config" text,
|
||||
"quick_actions" text,
|
||||
"notes" text,
|
||||
"enable_ssh" boolean DEFAULT true NOT NULL,
|
||||
"enable_rdp" boolean DEFAULT false NOT NULL,
|
||||
"enable_vnc" boolean DEFAULT false NOT NULL,
|
||||
"enable_telnet" boolean DEFAULT false NOT NULL,
|
||||
"ssh_port" integer DEFAULT 22,
|
||||
"rdp_port" integer DEFAULT 3389,
|
||||
"vnc_port" integer DEFAULT 5900,
|
||||
"telnet_port" integer DEFAULT 23,
|
||||
"rdp_credential_id" integer,
|
||||
"rdp_user" text,
|
||||
"rdp_password" text,
|
||||
"rdp_domain" text,
|
||||
"rdp_security" text,
|
||||
"rdp_ignore_cert" boolean DEFAULT false,
|
||||
"vnc_credential_id" integer,
|
||||
"vnc_password" text,
|
||||
"vnc_user" text,
|
||||
"telnet_user" text,
|
||||
"telnet_password" text,
|
||||
"telnet_credential_id" integer,
|
||||
"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" integer,
|
||||
"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" integer DEFAULT 0,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "ssh_data_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "network_topology" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"topology" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "notification_channels" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"config" text NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "opkssh_tokens" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"ssh_cert" text NOT NULL,
|
||||
"private_key" text NOT NULL,
|
||||
"email" text,
|
||||
"sub" text,
|
||||
"issuer" text,
|
||||
"audience" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text NOT NULL,
|
||||
"last_used" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "recent_activity" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"host_name" text,
|
||||
"timestamp" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "roles" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"display_name" text NOT NULL,
|
||||
"description" text,
|
||||
"is_system" boolean DEFAULT false NOT NULL,
|
||||
"permissions" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "roles_name_unique" UNIQUE("name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "session_recordings" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"user_id" varchar(255),
|
||||
"username" text,
|
||||
"access_id" integer,
|
||||
"started_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"ended_at" text,
|
||||
"duration" integer,
|
||||
"commands" text,
|
||||
"dangerous_actions" text,
|
||||
"recording_path" text,
|
||||
"protocol" varchar(255) DEFAULT 'ssh' NOT NULL,
|
||||
"format" text DEFAULT 'text' NOT NULL,
|
||||
"terminated_by_owner" boolean DEFAULT false,
|
||||
"termination_reason" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "session_share_participants" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"share_id" varchar(255) NOT NULL,
|
||||
"user_id" varchar(255),
|
||||
"guest_label" text,
|
||||
"joined_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"left_at" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "session_shares" (
|
||||
"id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"host_id" integer 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 DEFAULT 'read-only' NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text NOT NULL,
|
||||
"revoked_at" text,
|
||||
"last_joined_at" text,
|
||||
"join_count" integer DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT "session_shares_link_token_unique" UNIQUE("link_token")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "sessions" (
|
||||
"id" varchar(255) PRIMARY KEY 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" integer,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text NOT NULL,
|
||||
"last_active_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "settings" (
|
||||
"key" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"value" text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "shared_host_auth_overrides" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"protocol" varchar(255) DEFAULT 'ssh' NOT NULL,
|
||||
"credential_id" integer NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "shared_host_secrets" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"host_access_id" integer NOT NULL,
|
||||
"target_user_id" varchar(255) NOT NULL,
|
||||
"protocol" varchar(255) DEFAULT 'ssh' NOT NULL,
|
||||
"source_type" text DEFAULT 'credential' NOT NULL,
|
||||
"original_credential_id" integer,
|
||||
"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 DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "snippet_access" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"snippet_id" integer NOT NULL,
|
||||
"user_id" varchar(255),
|
||||
"role_id" integer,
|
||||
"granted_by" varchar(255) NOT NULL,
|
||||
"permission_level" text DEFAULT 'view' NOT NULL,
|
||||
"expires_at" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "snippet_folders" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"color" text,
|
||||
"icon" text,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "snippet_folders_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "snippets" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"description" text,
|
||||
"folder" text,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"host_filter" text,
|
||||
CONSTRAINT "snippets_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "ssh_credential_usage" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"credential_id" integer NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"used_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "ssh_credentials" (
|
||||
"id" serial PRIMARY KEY 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" integer DEFAULT 0 NOT NULL,
|
||||
"last_used" text,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "ssh_credentials_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "ssh_folders" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"color" text,
|
||||
"icon" text,
|
||||
"credential_id" integer,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "ssh_folders_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "sso_providers" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"display_order" integer DEFAULT 0 NOT NULL,
|
||||
"config" text NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "sync_tombstones" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"entity_type" text NOT NULL,
|
||||
"sync_id" varchar(255) NOT NULL,
|
||||
"deleted_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "termix_identities" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"handle" varchar(255) NOT NULL,
|
||||
"description" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "termix_identities_user_id_unique" UNIQUE("user_id"),
|
||||
CONSTRAINT "termix_identities_handle_unique" UNIQUE("handle")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "termix_identity_ca" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"identity_id" integer NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"public_key" text NOT NULL,
|
||||
"private_key" text NOT NULL,
|
||||
"validity_days" integer DEFAULT 90 NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "termix_identity_ca_identity_id_unique" UNIQUE("identity_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "termix_identity_keys" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"identity_id" integer 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 DEFAULT 'manual' NOT NULL,
|
||||
"credential_id" integer,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tmux_session_tags" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"host_id" integer NOT NULL,
|
||||
"session_name" text NOT NULL,
|
||||
"tag" text NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "transfer_recent" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"source_host_id" integer NOT NULL,
|
||||
"dest_host_id" integer NOT NULL,
|
||||
"dest_path" text NOT NULL,
|
||||
"dest_path_label" text NOT NULL,
|
||||
"last_used" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "trusted_devices" (
|
||||
"id" varchar(255) PRIMARY KEY 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 DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text NOT NULL,
|
||||
"last_used_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_open_tabs" (
|
||||
"id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"tab_type" text NOT NULL,
|
||||
"host_id" integer,
|
||||
"label" text NOT NULL,
|
||||
"tab_order" integer DEFAULT 0 NOT NULL,
|
||||
"backend_session_id" text,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_preferences" (
|
||||
"user_id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"reopen_tabs_on_login" boolean DEFAULT false NOT NULL,
|
||||
"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 DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_roles" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"role_id" integer NOT NULL,
|
||||
"granted_by" varchar(255),
|
||||
"granted_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
"id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"username" text NOT NULL,
|
||||
"password_hash" text NOT NULL,
|
||||
"is_admin" boolean DEFAULT false NOT NULL,
|
||||
"is_oidc" boolean DEFAULT false NOT NULL,
|
||||
"oidc_identifier" text,
|
||||
"sso_provider_id" integer,
|
||||
"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 DEFAULT false NOT NULL,
|
||||
"totp_backup_codes" text,
|
||||
"registered_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"donation_modal_dismissed" boolean DEFAULT false NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "vault_profiles" (
|
||||
"id" serial PRIMARY KEY 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 DEFAULT false NOT NULL,
|
||||
"sync_id" varchar(255),
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"updated_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
CONSTRAINT "vault_profiles_sync_id_unique" UNIQUE("sync_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "vault_tokens" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"profile_id" integer NOT NULL,
|
||||
"ssh_cert" text NOT NULL,
|
||||
"private_key" text NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"expires_at" text NOT NULL,
|
||||
"last_used" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "webauthn_credentials" (
|
||||
"id" varchar(255) PRIMARY KEY NOT NULL,
|
||||
"user_id" varchar(255) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"credential_id" text NOT NULL,
|
||||
"public_key" text NOT NULL,
|
||||
"counter" integer DEFAULT 0 NOT NULL,
|
||||
"device_type" text,
|
||||
"backed_up" boolean DEFAULT false NOT NULL,
|
||||
"transports" text,
|
||||
"user_verification" text DEFAULT 'preferred' NOT NULL,
|
||||
"created_at" text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
"last_used_at" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "alert_firings" ADD CONSTRAINT "alert_firings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."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 "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_host_health_checks_user_host" ON "host_health_checks" USING btree ("user_id","host_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_host_metrics_prefs_user_host" ON "host_metrics_preferences" USING btree ("user_id","host_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_opkssh_tokens_user_host" ON "opkssh_tokens" USING btree ("user_id","host_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "shared_host_auth_overrides_host_user_protocol_unique" ON "shared_host_auth_overrides" USING btree ("host_id","user_id","protocol");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_shared_host_secrets_scope" ON "shared_host_secrets" USING btree ("host_access_id","target_user_id","protocol");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_user_roles_user_role" ON "user_roles" USING btree ("user_id","role_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "idx_vault_tokens_user_profile" ON "vault_tokens" USING btree ("user_id","profile_id");
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1785738871078,
|
||||
"tag": "0000_jazzy_infant_terrible",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,836 @@
|
||||
CREATE TABLE `alert_firings` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`rule_id` integer NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`host_name` text NOT NULL,
|
||||
`fired_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`resolved_at` text,
|
||||
`value` real,
|
||||
`message` text NOT NULL,
|
||||
`severity` text DEFAULT 'warning' NOT NULL,
|
||||
`acknowledged` integer DEFAULT false NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`rule_id`) REFERENCES `alert_rules`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `alert_rule_channels` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`rule_id` integer NOT NULL,
|
||||
`channel_id` integer NOT NULL,
|
||||
FOREIGN KEY (`rule_id`) REFERENCES `alert_rules`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`channel_id`) REFERENCES `notification_channels`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `alert_rules` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer,
|
||||
`name` text NOT NULL,
|
||||
`enabled` integer DEFAULT true NOT NULL,
|
||||
`trigger_type` text NOT NULL,
|
||||
`threshold_value` real,
|
||||
`threshold_duration_seconds` integer,
|
||||
`cooldown_minutes` integer DEFAULT 15 NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `api_keys` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`token_hash` text NOT NULL,
|
||||
`token_prefix` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text,
|
||||
`last_used_at` text,
|
||||
`is_active` integer DEFAULT true NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `audit_logs` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text,
|
||||
`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` integer NOT NULL,
|
||||
`error_message` text,
|
||||
`timestamp` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `c2s_tunnel_presets` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`platform` text,
|
||||
`computer_name` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `command_history` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`command` text NOT NULL,
|
||||
`executed_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `dashboard_service_links` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`label` text NOT NULL,
|
||||
`url` text NOT NULL,
|
||||
`order` integer DEFAULT 0 NOT NULL,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `dashboard_service_links_sync_id_unique` ON `dashboard_service_links` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `dismissed_alerts` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`alert_id` text NOT NULL,
|
||||
`dismissed_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `file_manager_pinned` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`path` text NOT NULL,
|
||||
`pinned_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `file_manager_recent` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`path` text NOT NULL,
|
||||
`last_opened` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `file_manager_shortcuts` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`path` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `homepage_items` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`type_id` text NOT NULL,
|
||||
`title` text,
|
||||
`config` text DEFAULT '{}' NOT NULL,
|
||||
`folder_id` integer,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `homepage_items_sync_id_unique` ON `homepage_items` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `homepage_layouts` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`layout` text DEFAULT '{}' NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `homepage_layouts_user_id_unique` ON `homepage_layouts` (`user_id`);--> statement-breakpoint
|
||||
CREATE TABLE `host_access` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`user_id` text,
|
||||
`role_id` integer,
|
||||
`granted_by` text NOT NULL,
|
||||
`permission_level` text DEFAULT 'connect' NOT NULL,
|
||||
`expires_at` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`last_accessed_at` text,
|
||||
`access_count` integer DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `host_health_checks` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`checks` text NOT NULL,
|
||||
`interval_seconds` integer DEFAULT 300 NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_host_health_checks_user_host` ON `host_health_checks` (`user_id`,`host_id`);--> statement-breakpoint
|
||||
CREATE TABLE `host_health_history` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`check_id` text NOT NULL,
|
||||
`ts` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`ok` integer NOT NULL,
|
||||
`latency_ms` integer,
|
||||
`detail` text,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `host_metrics_history` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`ts` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`cpu_percent` real,
|
||||
`mem_percent` real,
|
||||
`disk_percent` real,
|
||||
`net_rx_bytes` integer,
|
||||
`net_tx_bytes` integer,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `host_metrics_preferences` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`layout` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_host_metrics_prefs_user_host` ON `host_metrics_preferences` (`user_id`,`host_id`);--> statement-breakpoint
|
||||
CREATE TABLE `ssh_data` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`connection_type` text DEFAULT 'ssh' NOT NULL,
|
||||
`name` text,
|
||||
`ip` text NOT NULL,
|
||||
`port` integer NOT NULL,
|
||||
`username` text NOT NULL,
|
||||
`folder` text,
|
||||
`tags` text,
|
||||
`pin` integer DEFAULT false NOT NULL,
|
||||
`auth_type` text NOT NULL,
|
||||
`use_warpgate` integer DEFAULT false NOT NULL,
|
||||
`share_ssh_auth` integer DEFAULT false NOT NULL,
|
||||
`force_keyboard_interactive` text,
|
||||
`password` text,
|
||||
`key` text(8192),
|
||||
`key_password` text,
|
||||
`key_type` text,
|
||||
`sudo_password` text,
|
||||
`autostart_password` text,
|
||||
`autostart_key` text(8192),
|
||||
`autostart_key_password` text,
|
||||
`credential_id` integer,
|
||||
`override_credential_username` integer,
|
||||
`vault_profile_id` integer,
|
||||
`enable_terminal` integer DEFAULT true NOT NULL,
|
||||
`enable_session_logging` integer DEFAULT true NOT NULL,
|
||||
`allow_session_sharing` integer DEFAULT true NOT NULL,
|
||||
`enable_command_history` integer DEFAULT true NOT NULL,
|
||||
`enable_tunnel` integer DEFAULT true NOT NULL,
|
||||
`tunnel_connections` text,
|
||||
`jump_hosts` text,
|
||||
`enable_file_manager` integer DEFAULT true NOT NULL,
|
||||
`scp_legacy` integer DEFAULT false NOT NULL,
|
||||
`enable_docker` integer DEFAULT false NOT NULL,
|
||||
`enable_tmux_monitor` integer DEFAULT false NOT NULL,
|
||||
`show_terminal_in_sidebar` integer DEFAULT true NOT NULL,
|
||||
`show_file_manager_in_sidebar` integer DEFAULT false NOT NULL,
|
||||
`show_tunnel_in_sidebar` integer DEFAULT false NOT NULL,
|
||||
`show_docker_in_sidebar` integer DEFAULT false NOT NULL,
|
||||
`show_server_stats_in_sidebar` integer DEFAULT false NOT NULL,
|
||||
`default_path` text,
|
||||
`stats_config` text,
|
||||
`docker_config` text,
|
||||
`enable_proxmox` integer DEFAULT false NOT NULL,
|
||||
`proxmox_config` text,
|
||||
`terminal_config` text,
|
||||
`quick_actions` text,
|
||||
`notes` text,
|
||||
`enable_ssh` integer DEFAULT true NOT NULL,
|
||||
`enable_rdp` integer DEFAULT false NOT NULL,
|
||||
`enable_vnc` integer DEFAULT false NOT NULL,
|
||||
`enable_telnet` integer DEFAULT false NOT NULL,
|
||||
`ssh_port` integer DEFAULT 22,
|
||||
`rdp_port` integer DEFAULT 3389,
|
||||
`vnc_port` integer DEFAULT 5900,
|
||||
`telnet_port` integer DEFAULT 23,
|
||||
`rdp_credential_id` integer,
|
||||
`rdp_user` text,
|
||||
`rdp_password` text,
|
||||
`rdp_domain` text,
|
||||
`rdp_security` text,
|
||||
`rdp_ignore_cert` integer DEFAULT false,
|
||||
`vnc_credential_id` integer,
|
||||
`vnc_password` text,
|
||||
`vnc_user` text,
|
||||
`telnet_user` text,
|
||||
`telnet_password` text,
|
||||
`telnet_credential_id` integer,
|
||||
`rdp_auth_type` text,
|
||||
`vnc_auth_type` text,
|
||||
`telnet_auth_type` text,
|
||||
`domain` text,
|
||||
`security` text,
|
||||
`ignore_cert` integer DEFAULT false,
|
||||
`guacamole_config` text,
|
||||
`use_socks5` integer,
|
||||
`socks5_host` text,
|
||||
`socks5_port` integer,
|
||||
`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` integer DEFAULT 0,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null,
|
||||
FOREIGN KEY (`vault_profile_id`) REFERENCES `vault_profiles`(`id`) ON UPDATE no action ON DELETE set null,
|
||||
FOREIGN KEY (`rdp_credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null,
|
||||
FOREIGN KEY (`vnc_credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null,
|
||||
FOREIGN KEY (`telnet_credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `ssh_data_sync_id_unique` ON `ssh_data` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `network_topology` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`topology` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `notification_channels` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`enabled` integer DEFAULT true NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `opkssh_tokens` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`ssh_cert` text(8192) NOT NULL,
|
||||
`private_key` text(8192) NOT NULL,
|
||||
`email` text,
|
||||
`sub` text,
|
||||
`issuer` text,
|
||||
`audience` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text NOT NULL,
|
||||
`last_used` text,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_opkssh_tokens_user_host` ON `opkssh_tokens` (`user_id`,`host_id`);--> statement-breakpoint
|
||||
CREATE TABLE `recent_activity` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`host_name` text,
|
||||
`timestamp` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `roles` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`display_name` text NOT NULL,
|
||||
`description` text,
|
||||
`is_system` integer DEFAULT false NOT NULL,
|
||||
`permissions` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `roles_name_unique` ON `roles` (`name`);--> statement-breakpoint
|
||||
CREATE TABLE `session_recordings` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`user_id` text,
|
||||
`username` text,
|
||||
`access_id` integer,
|
||||
`started_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`ended_at` text,
|
||||
`duration` integer,
|
||||
`commands` text,
|
||||
`dangerous_actions` text,
|
||||
`recording_path` text,
|
||||
`protocol` text DEFAULT 'ssh' NOT NULL,
|
||||
`format` text DEFAULT 'text' NOT NULL,
|
||||
`terminated_by_owner` integer DEFAULT false,
|
||||
`termination_reason` text,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE set null,
|
||||
FOREIGN KEY (`access_id`) REFERENCES `host_access`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `session_share_participants` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`share_id` text NOT NULL,
|
||||
`user_id` text,
|
||||
`guest_label` text,
|
||||
`joined_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`left_at` text,
|
||||
FOREIGN KEY (`share_id`) REFERENCES `session_shares`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `session_shares` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`owner_user_id` text NOT NULL,
|
||||
`protocol` text NOT NULL,
|
||||
`session_id` text NOT NULL,
|
||||
`tab_instance_id` text,
|
||||
`share_type` text NOT NULL,
|
||||
`target_user_id` text,
|
||||
`link_token` text,
|
||||
`permission_level` text DEFAULT 'read-only' NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text NOT NULL,
|
||||
`revoked_at` text,
|
||||
`last_joined_at` text,
|
||||
`join_count` integer DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`owner_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`target_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `session_shares_link_token_unique` ON `session_shares` (`link_token`);--> statement-breakpoint
|
||||
CREATE TABLE `sessions` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text 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` integer,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text NOT NULL,
|
||||
`last_active_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `settings` (
|
||||
`key` text PRIMARY KEY NOT NULL,
|
||||
`value` text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `shared_host_auth_overrides` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`protocol` text DEFAULT 'ssh' NOT NULL,
|
||||
`credential_id` integer NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `shared_host_auth_overrides_host_user_protocol_unique` ON `shared_host_auth_overrides` (`host_id`,`user_id`,`protocol`);--> statement-breakpoint
|
||||
CREATE TABLE `shared_host_secrets` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`host_access_id` integer NOT NULL,
|
||||
`target_user_id` text NOT NULL,
|
||||
`protocol` text DEFAULT 'ssh' NOT NULL,
|
||||
`source_type` text DEFAULT 'credential' NOT NULL,
|
||||
`original_credential_id` integer,
|
||||
`encrypted_username` text,
|
||||
`encrypted_auth_type` text,
|
||||
`encrypted_password` text,
|
||||
`encrypted_key` text(16384),
|
||||
`encrypted_key_password` text,
|
||||
`encrypted_key_type` text,
|
||||
`encrypted_domain` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`host_access_id`) REFERENCES `host_access`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`target_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`original_credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_shared_host_secrets_scope` ON `shared_host_secrets` (`host_access_id`,`target_user_id`,`protocol`);--> statement-breakpoint
|
||||
CREATE TABLE `snippet_access` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`snippet_id` integer NOT NULL,
|
||||
`user_id` text,
|
||||
`role_id` integer,
|
||||
`granted_by` text NOT NULL,
|
||||
`permission_level` text DEFAULT 'view' NOT NULL,
|
||||
`expires_at` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`snippet_id`) REFERENCES `snippets`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `snippet_folders` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`color` text,
|
||||
`icon` text,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `snippet_folders_sync_id_unique` ON `snippet_folders` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `snippets` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`description` text,
|
||||
`folder` text,
|
||||
`order` integer DEFAULT 0 NOT NULL,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`host_filter` text,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `snippets_sync_id_unique` ON `snippets` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `ssh_credential_usage` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`credential_id` integer NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`used_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `ssh_credentials` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`description` text,
|
||||
`folder` text,
|
||||
`tags` text,
|
||||
`auth_type` text NOT NULL,
|
||||
`username` text,
|
||||
`password` text,
|
||||
`key` text(16384),
|
||||
`private_key` text(16384),
|
||||
`public_key` text(4096),
|
||||
`key_password` text,
|
||||
`key_type` text,
|
||||
`detected_key_type` text,
|
||||
`cert_public_key` text(8192),
|
||||
`usage_count` integer DEFAULT 0 NOT NULL,
|
||||
`last_used` text,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `ssh_credentials_sync_id_unique` ON `ssh_credentials` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `ssh_folders` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`color` text,
|
||||
`icon` text,
|
||||
`credential_id` integer,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `ssh_folders_sync_id_unique` ON `ssh_folders` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `sso_providers` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`enabled` integer DEFAULT true NOT NULL,
|
||||
`display_order` integer DEFAULT 0 NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `sync_tombstones` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`entity_type` text NOT NULL,
|
||||
`sync_id` text NOT NULL,
|
||||
`deleted_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `termix_identities` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`handle` text NOT NULL,
|
||||
`description` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `termix_identities_user_id_unique` ON `termix_identities` (`user_id`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `termix_identities_handle_unique` ON `termix_identities` (`handle`);--> statement-breakpoint
|
||||
CREATE TABLE `termix_identity_ca` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`identity_id` integer NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`public_key` text(4096) NOT NULL,
|
||||
`private_key` text(8192) NOT NULL,
|
||||
`validity_days` integer DEFAULT 90 NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`identity_id`) REFERENCES `termix_identities`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `termix_identity_ca_identity_id_unique` ON `termix_identity_ca` (`identity_id`);--> statement-breakpoint
|
||||
CREATE TABLE `termix_identity_keys` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`identity_id` integer NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`public_key` text(8192) NOT NULL,
|
||||
`key_type` text NOT NULL,
|
||||
`algorithm` text NOT NULL,
|
||||
`label` text,
|
||||
`comment` text,
|
||||
`source` text DEFAULT 'manual' NOT NULL,
|
||||
`credential_id` integer,
|
||||
`enabled` integer DEFAULT true NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`identity_id`) REFERENCES `termix_identities`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`credential_id`) REFERENCES `ssh_credentials`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `tmux_session_tags` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`host_id` integer NOT NULL,
|
||||
`session_name` text NOT NULL,
|
||||
`tag` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `transfer_recent` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`source_host_id` integer NOT NULL,
|
||||
`dest_host_id` integer NOT NULL,
|
||||
`dest_path` text NOT NULL,
|
||||
`dest_path_label` text NOT NULL,
|
||||
`last_used` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`source_host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`dest_host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `trusted_devices` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`device_fingerprint` text NOT NULL,
|
||||
`device_type` text NOT NULL,
|
||||
`device_info` text NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text NOT NULL,
|
||||
`last_used_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user_open_tabs` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`tab_type` text NOT NULL,
|
||||
`host_id` integer,
|
||||
`label` text NOT NULL,
|
||||
`tab_order` integer DEFAULT 0 NOT NULL,
|
||||
`backend_session_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`host_id`) REFERENCES `ssh_data`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user_preferences` (
|
||||
`user_id` text PRIMARY KEY NOT NULL,
|
||||
`reopen_tabs_on_login` integer DEFAULT false NOT NULL,
|
||||
`theme` text,
|
||||
`font_size` text,
|
||||
`accent_color` text,
|
||||
`language` text,
|
||||
`storage_mode` text,
|
||||
`command_autocomplete` integer,
|
||||
`command_palette_enabled` integer,
|
||||
`show_host_tags` integer,
|
||||
`host_tray_on_click` integer,
|
||||
`pin_app_rail` integer,
|
||||
`expand_app_rail_on_hover` integer,
|
||||
`folders_collapsed` integer,
|
||||
`confirm_snippet_execution` integer,
|
||||
`disable_update_check` integer,
|
||||
`confirm_tab_close` integer,
|
||||
`hidden_rail_tabs` text,
|
||||
`compact_host_view` integer,
|
||||
`status_color_scheme` text,
|
||||
`custom_themes` text,
|
||||
`custom_keybindings` text,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user_roles` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`role_id` integer NOT NULL,
|
||||
`granted_by` text,
|
||||
`granted_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`granted_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_user_roles_user_role` ON `user_roles` (`user_id`,`role_id`);--> statement-breakpoint
|
||||
CREATE TABLE `users` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`username` text NOT NULL,
|
||||
`password_hash` text NOT NULL,
|
||||
`is_admin` integer DEFAULT false NOT NULL,
|
||||
`is_oidc` integer DEFAULT false NOT NULL,
|
||||
`oidc_identifier` text,
|
||||
`sso_provider_id` integer,
|
||||
`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` integer DEFAULT false NOT NULL,
|
||||
`totp_backup_codes` text,
|
||||
`registered_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`donation_modal_dismissed` integer DEFAULT false NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `vault_profiles` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text 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` integer DEFAULT false NOT NULL,
|
||||
`sync_id` text,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `vault_profiles_sync_id_unique` ON `vault_profiles` (`sync_id`);--> statement-breakpoint
|
||||
CREATE TABLE `vault_tokens` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`profile_id` integer NOT NULL,
|
||||
`ssh_cert` text(8192) NOT NULL,
|
||||
`private_key` text(8192) NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`expires_at` text NOT NULL,
|
||||
`last_used` text,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||
FOREIGN KEY (`profile_id`) REFERENCES `vault_profiles`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `idx_vault_tokens_user_profile` ON `vault_tokens` (`user_id`,`profile_id`);--> statement-breakpoint
|
||||
CREATE TABLE `webauthn_credentials` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`credential_id` text NOT NULL,
|
||||
`public_key` text NOT NULL,
|
||||
`counter` integer DEFAULT 0 NOT NULL,
|
||||
`device_type` text,
|
||||
`backed_up` integer DEFAULT false NOT NULL,
|
||||
`transports` text,
|
||||
`user_verification` text DEFAULT 'preferred' NOT NULL,
|
||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
`last_used_at` text,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1785738870735,
|
||||
"tag": "0000_clever_hercules",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,12 @@
|
||||
"output": "release"
|
||||
},
|
||||
"asar": true,
|
||||
"asarUnpack": ["dist/backend/**/*", "node_modules/**/*"],
|
||||
"asarUnpack": [
|
||||
"dist/backend/**/*",
|
||||
"node_modules/**/*",
|
||||
"public/icons/**/*",
|
||||
"public/icon.*"
|
||||
],
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"electron/**/*",
|
||||
@@ -23,14 +28,12 @@
|
||||
"!dist/icon-mac.png",
|
||||
"!public/icon-mac.png",
|
||||
"!dist/icon.ico",
|
||||
"!public/icon.ico",
|
||||
"!dist/icon.icns",
|
||||
"!public/icon.icns",
|
||||
"!dist/icons/**/*",
|
||||
"!public/icons/**/*"
|
||||
"!public/icon.icns"
|
||||
],
|
||||
"extraMetadata": {
|
||||
"main": "electron/main.cjs"
|
||||
"main": "electron/main.cjs",
|
||||
"type": "commonjs"
|
||||
},
|
||||
"buildDependenciesFromSource": false,
|
||||
"nodeGypRebuild": false,
|
||||
@@ -55,6 +58,8 @@
|
||||
"artifactName": "termix_windows_${arch}_nsis.${ext}",
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"installerIcon": "public/icon.ico",
|
||||
"uninstallerIcon": "public/icon.ico",
|
||||
"shortcutName": "Termix",
|
||||
"uninstallDisplayName": "Termix"
|
||||
},
|
||||
@@ -84,8 +89,8 @@
|
||||
"desktop": {
|
||||
"entry": {
|
||||
"Name": "Termix",
|
||||
"Comment": "A web-based server management platform",
|
||||
"Keywords": "terminal;ssh;server;management;",
|
||||
"Comment": "Self-hosted SSH and remote desktop management. ",
|
||||
"Keywords": "docker;ssh;terminal;telnet;self-hosted;rdp;file-management;vnc;ssh-tunnel;server-stats;termix",
|
||||
"StartupWMClass": "termix"
|
||||
}
|
||||
}
|
||||
@@ -112,8 +117,8 @@
|
||||
"category": "public.app-category.developer-tools",
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "build/entitlements.mac.inherit.plist",
|
||||
"entitlements": "packaging/build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "packaging/build/entitlements.mac.inherit.plist",
|
||||
"type": "distribution",
|
||||
"minimumSystemVersion": "10.15",
|
||||
"mergeASARs": false,
|
||||
@@ -124,11 +129,12 @@
|
||||
"artifactName": "termix_macos_${arch}_dmg.${ext}",
|
||||
"sign": true
|
||||
},
|
||||
"afterSign": "build/notarize.cjs",
|
||||
"afterPack": "packaging/build/after-pack.cjs",
|
||||
"afterSign": "packaging/build/notarize.cjs",
|
||||
"mas": {
|
||||
"provisioningProfile": "build/Termix_Mac_App_Store.provisionprofile",
|
||||
"entitlements": "build/entitlements.mas.plist",
|
||||
"entitlementsInherit": "build/entitlements.mas.inherit.plist",
|
||||
"provisioningProfile": "packaging/build/Termix_Mac_App_Store.provisionprofile",
|
||||
"entitlements": "packaging/build/entitlements.mas.plist",
|
||||
"entitlementsInherit": "packaging/build/entitlements.mas.inherit.plist",
|
||||
"hardenedRuntime": false,
|
||||
"gatekeeperAssess": false,
|
||||
"type": "distribution",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
function getUnpackedAppRoot(appRoot) {
|
||||
return appRoot.replace(
|
||||
/app(-[a-z0-9]+)?\.asar(?!\.unpacked)/,
|
||||
"app$1.asar.unpacked",
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { getUnpackedAppRoot };
|
||||
@@ -31,6 +31,13 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
startC2SAutoStartTunnels: () =>
|
||||
ipcRenderer.invoke("start-c2s-autostart-tunnels"),
|
||||
|
||||
onRemoteSyncStatusChanged: (callback) => {
|
||||
const listener = (_event, status) => callback(status);
|
||||
ipcRenderer.on("remote-sync-status-changed", listener);
|
||||
return () =>
|
||||
ipcRenderer.removeListener("remote-sync-status-changed", listener);
|
||||
},
|
||||
|
||||
clearSessionCookies: () => ipcRenderer.invoke("clear-session-cookies"),
|
||||
getSessionCookie: (name, targetUrl) =>
|
||||
ipcRenderer.invoke("get-session-cookie", name, targetUrl),
|
||||
@@ -43,6 +50,29 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
timeoutMs,
|
||||
),
|
||||
|
||||
oidcSystemBrowserAuth: (authUrl, callbackPort) =>
|
||||
ipcRenderer.invoke("oidc-system-browser-auth", authUrl, callbackPort),
|
||||
|
||||
openExternalEditor: (fileData) =>
|
||||
ipcRenderer.invoke("open-external-editor", fileData),
|
||||
closeExternalEditor: (editId) =>
|
||||
ipcRenderer.invoke("close-external-editor", editId),
|
||||
onExternalEditorSaved: (callback) => {
|
||||
const listener = (_event, payload) => callback(payload);
|
||||
ipcRenderer.on("external-editor-saved", listener);
|
||||
return () => ipcRenderer.removeListener("external-editor-saved", listener);
|
||||
},
|
||||
|
||||
showSaveDialog: (options) => ipcRenderer.invoke("show-save-dialog", options),
|
||||
showOpenDialog: (options) => ipcRenderer.invoke("show-open-dialog", options),
|
||||
createTempFile: (fileData) =>
|
||||
ipcRenderer.invoke("create-temp-file", fileData),
|
||||
createTempFolder: (folderData) =>
|
||||
ipcRenderer.invoke("create-temp-folder", folderData),
|
||||
startDragToDesktop: (dragData) =>
|
||||
ipcRenderer.invoke("start-drag-to-desktop", dragData),
|
||||
cleanupTempFile: (tempId) => ipcRenderer.invoke("cleanup-temp-file", tempId),
|
||||
|
||||
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
const SYNCED_ENTITY_TYPES = Object.freeze([
|
||||
// Ordered by reference dependency: hosts and snippets resolve credential,
|
||||
// vault and folder syncIds, so those have to exist on the other side first.
|
||||
"sshCredentials",
|
||||
"vaultProfiles",
|
||||
"sshFolders",
|
||||
"snippetFolders",
|
||||
"hosts",
|
||||
"snippets",
|
||||
"dashboardServiceLinks",
|
||||
"homepageItems",
|
||||
"userPreferences",
|
||||
]);
|
||||
|
||||
module.exports = { SYNCED_ENTITY_TYPES };
|
||||
@@ -0,0 +1,573 @@
|
||||
// Remote sync engine for the desktop app's optional connection to a
|
||||
// self-hosted Termix server. Runs entirely in the Electron main process:
|
||||
// - Holds the remote JWT (safeStorage-encrypted on disk, never exposed to
|
||||
// the renderer's localStorage) and the local embedded backend's JWT
|
||||
// (cached in memory only, handed over by the renderer at local-login
|
||||
// time via notify-local-login).
|
||||
// - On a timer, pulls + pushes each synced entity type between the
|
||||
// embedded backend (always localhost:30001) and the configured remote
|
||||
// server, reconciling by syncId with last-write-wins on updatedAt, and
|
||||
// propagating tombstones (deletions) in both directions.
|
||||
// - Pushes connection/sync status to the renderer via IPC so the Settings
|
||||
// UI and a global banner can reflect it without polling.
|
||||
|
||||
const { app, safeStorage } = require("electron");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { SYNCED_ENTITY_TYPES } = require("./remote-sync-entities.cjs");
|
||||
|
||||
const SYNC_INTERVAL_MS = 90 * 1000;
|
||||
const EMBEDDED_BASE_URL = "http://127.0.0.1:30001";
|
||||
|
||||
function dataPath(filename) {
|
||||
return path.join(app.getPath("userData"), filename);
|
||||
}
|
||||
|
||||
function readJson(filePath, fallback) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return fallback;
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJson(filePath, value) {
|
||||
const userDataPath = app.getPath("userData");
|
||||
if (!fs.existsSync(userDataPath)) {
|
||||
fs.mkdirSync(userDataPath, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(filePath, JSON.stringify(value, null, 2));
|
||||
}
|
||||
|
||||
function getDesktopSettingsPath() {
|
||||
return dataPath("desktop-settings.json");
|
||||
}
|
||||
|
||||
function getRemoteSyncConfigPath() {
|
||||
return dataPath("remote-sync-config.json");
|
||||
}
|
||||
|
||||
function getRemoteSyncCredentialPath() {
|
||||
return dataPath("remote-sync-credential.json");
|
||||
}
|
||||
|
||||
function getRemoteSyncStatePath() {
|
||||
return dataPath("remote-sync-state.json");
|
||||
}
|
||||
|
||||
function getDesktopSettings() {
|
||||
return readJson(getDesktopSettingsPath(), {
|
||||
defaultConnectionOrigin: "local",
|
||||
migrationNoticeAcknowledged: false,
|
||||
});
|
||||
}
|
||||
|
||||
function saveDesktopSettings(settings) {
|
||||
writeJson(getDesktopSettingsPath(), settings);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
function getRemoteSyncConfig() {
|
||||
return readJson(getRemoteSyncConfigPath(), null);
|
||||
}
|
||||
|
||||
function saveRemoteSyncConfig(config) {
|
||||
writeJson(getRemoteSyncConfigPath(), config);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
function clearRemoteSyncConfig() {
|
||||
try {
|
||||
fs.unlinkSync(getRemoteSyncConfigPath());
|
||||
} catch {
|
||||
// already absent
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
function getSafeStorageAvailable() {
|
||||
try {
|
||||
return safeStorage.isEncryptionAvailable();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveRemoteSyncJwt(token) {
|
||||
if (!getSafeStorageAvailable()) {
|
||||
return { success: false, error: "Encryption unavailable on this system" };
|
||||
}
|
||||
writeJson(getRemoteSyncCredentialPath(), {
|
||||
encrypted: true,
|
||||
value: safeStorage.encryptString(token).toString("base64"),
|
||||
obtainedAt: new Date().toISOString(),
|
||||
});
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
function getRemoteSyncJwt() {
|
||||
const record = readJson(getRemoteSyncCredentialPath(), null);
|
||||
if (!record?.encrypted || !getSafeStorageAvailable()) return null;
|
||||
try {
|
||||
return safeStorage.decryptString(Buffer.from(record.value, "base64"));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function clearRemoteSyncJwt() {
|
||||
try {
|
||||
fs.unlinkSync(getRemoteSyncCredentialPath());
|
||||
} catch {
|
||||
// already absent
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async function getRemoteSyncUserInfo() {
|
||||
const config = getRemoteSyncConfig();
|
||||
const token = getRemoteSyncJwt();
|
||||
if (!config?.serverUrl || !token || isJwtExpiredOrExpiringSoon(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const baseUrl = config.serverUrl.replace(/\/$/, "");
|
||||
const userResponse = await fetch(`${baseUrl}/users/me`, {
|
||||
headers: { Authorization: `Bearer ${token}`, "X-Electron-App": "true" },
|
||||
});
|
||||
if (!userResponse.ok) return null;
|
||||
|
||||
const user = await userResponse.json();
|
||||
const rolesResponse = await fetch(
|
||||
`${baseUrl}/rbac/users/${encodeURIComponent(user.userId)}/roles`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}`, "X-Electron-App": "true" },
|
||||
},
|
||||
);
|
||||
const roles = rolesResponse.ok
|
||||
? (await rolesResponse.json()).roles || []
|
||||
: [];
|
||||
|
||||
return {
|
||||
userId: user.userId,
|
||||
username: user.username,
|
||||
is_admin: !!user.is_admin,
|
||||
is_oidc: !!user.is_oidc,
|
||||
is_dual_auth: !!user.is_dual_auth,
|
||||
totp_enabled: !!user.totp_enabled,
|
||||
roles,
|
||||
};
|
||||
}
|
||||
|
||||
function decodeJwtExpiry(token) {
|
||||
try {
|
||||
const payloadB64 = token.split(".")[1];
|
||||
const payload = JSON.parse(
|
||||
Buffer.from(payloadB64, "base64").toString("utf8"),
|
||||
);
|
||||
return typeof payload.exp === "number" ? payload.exp * 1000 : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isJwtExpiredOrExpiringSoon(token, marginMs = 60 * 1000) {
|
||||
const expiresAt = decodeJwtExpiry(token);
|
||||
if (expiresAt === null) return false;
|
||||
return Date.now() + marginMs >= expiresAt;
|
||||
}
|
||||
|
||||
class RemoteSyncEngine {
|
||||
constructor(getMainWindow) {
|
||||
this.getMainWindow = getMainWindow;
|
||||
this.localJwt = null;
|
||||
this.timer = null;
|
||||
this.syncing = false;
|
||||
this.status = {
|
||||
connected: false,
|
||||
syncing: false,
|
||||
lastSyncedAt: null,
|
||||
lastError: null,
|
||||
needsReauth: false,
|
||||
};
|
||||
}
|
||||
|
||||
setLocalJwt(token) {
|
||||
this.localJwt = token || null;
|
||||
}
|
||||
|
||||
emitStatus() {
|
||||
const win = this.getMainWindow?.();
|
||||
if (!win || win.isDestroyed()) return;
|
||||
win.webContents.send("remote-sync-status-changed", this.status);
|
||||
}
|
||||
|
||||
updateStatus(patch) {
|
||||
this.status = { ...this.status, ...patch };
|
||||
this.emitStatus();
|
||||
}
|
||||
|
||||
start() {
|
||||
const config = getRemoteSyncConfig();
|
||||
this.status.connected = !!config?.serverUrl;
|
||||
if (this.timer) clearInterval(this.timer);
|
||||
this.timer = setInterval(() => this.syncNow(), SYNC_INTERVAL_MS);
|
||||
if (config?.serverUrl) {
|
||||
// Fire an initial sync shortly after startup rather than waiting a
|
||||
// full interval, but don't block app boot on it.
|
||||
setTimeout(() => this.syncNow(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
async syncNow() {
|
||||
if (this.syncing) return this.status;
|
||||
const config = getRemoteSyncConfig();
|
||||
if (!config?.serverUrl) {
|
||||
this.updateStatus({ connected: false, syncing: false });
|
||||
return this.status;
|
||||
}
|
||||
|
||||
const remoteJwt = getRemoteSyncJwt();
|
||||
if (!remoteJwt) {
|
||||
this.updateStatus({
|
||||
connected: true,
|
||||
syncing: false,
|
||||
needsReauth: true,
|
||||
lastError: "Not signed in to remote server",
|
||||
});
|
||||
return this.status;
|
||||
}
|
||||
if (isJwtExpiredOrExpiringSoon(remoteJwt)) {
|
||||
this.updateStatus({
|
||||
connected: true,
|
||||
syncing: false,
|
||||
needsReauth: true,
|
||||
lastError: "Remote session expired",
|
||||
});
|
||||
return this.status;
|
||||
}
|
||||
if (!this.localJwt) {
|
||||
// Local login hasn't handed us a token yet -- this is expected for the
|
||||
// first tick or two right after a cold boot (renderer hasn't finished
|
||||
// its own session check yet), but if it never arrives (e.g. a gap in
|
||||
// whichever code path establishes the local session), sync would
|
||||
// otherwise silently no-op forever with no visible error. Surface it
|
||||
// as a normal, non-alarming "not synced yet" status rather than
|
||||
// leaving lastSyncedAt/lastError untouched.
|
||||
this.updateStatus({
|
||||
connected: true,
|
||||
syncing: false,
|
||||
lastError: "Waiting for local session",
|
||||
});
|
||||
return this.status;
|
||||
}
|
||||
|
||||
this.syncing = true;
|
||||
this.updateStatus({ connected: true, syncing: true, lastError: null });
|
||||
|
||||
try {
|
||||
const state = readJson(getRemoteSyncStatePath(), { entities: {} });
|
||||
let sawAuthFailure = false;
|
||||
|
||||
for (const entityType of SYNCED_ENTITY_TYPES) {
|
||||
const entityState = state.entities[entityType] || {
|
||||
lastPulledAt: null,
|
||||
lastPushedAt: null,
|
||||
};
|
||||
|
||||
const result = await this.syncEntity({
|
||||
entityType,
|
||||
remoteBaseUrl: config.serverUrl.replace(/\/$/, ""),
|
||||
remoteJwt,
|
||||
since: entityState.lastPulledAt,
|
||||
});
|
||||
|
||||
if (result.authFailure) {
|
||||
sawAuthFailure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
state.entities[entityType] = {
|
||||
lastPulledAt: result.syncedAt,
|
||||
lastPushedAt: result.syncedAt,
|
||||
};
|
||||
}
|
||||
|
||||
if (sawAuthFailure) {
|
||||
this.updateStatus({
|
||||
syncing: false,
|
||||
needsReauth: true,
|
||||
lastError: "Remote server rejected the session",
|
||||
});
|
||||
return this.status;
|
||||
}
|
||||
|
||||
writeJson(getRemoteSyncStatePath(), state);
|
||||
writeJson(getRemoteSyncConfigPath(), {
|
||||
...config,
|
||||
lastSyncedAt: new Date().toISOString(),
|
||||
lastSyncStatus: "ok",
|
||||
lastSyncError: null,
|
||||
});
|
||||
|
||||
this.updateStatus({
|
||||
connected: true,
|
||||
syncing: false,
|
||||
needsReauth: false,
|
||||
lastSyncedAt: new Date().toISOString(),
|
||||
lastError: null,
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
writeJson(getRemoteSyncConfigPath(), {
|
||||
...config,
|
||||
lastSyncStatus: "error",
|
||||
lastSyncError: message,
|
||||
});
|
||||
this.updateStatus({ syncing: false, lastError: message });
|
||||
} finally {
|
||||
this.syncing = false;
|
||||
}
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
async fetchJson(url, token, options = {}) {
|
||||
const res = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
...(options.headers || {}),
|
||||
},
|
||||
});
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
const err = new Error(`Auth failed (${res.status})`);
|
||||
err.authFailure = true;
|
||||
throw err;
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(`Request failed (${res.status}): ${url}`);
|
||||
}
|
||||
|
||||
const text = await res.text();
|
||||
// A reverse-proxy SSO in front of the remote server (Pangolin, Authelia,
|
||||
// etc.) can intercept even an authenticated, Bearer-token'd request and
|
||||
// serve its own login page instead of forwarding to Termix -- that comes
|
||||
// back as a normal 200 OK, so the status checks above don't catch it.
|
||||
// This is NOT the same as needsReauth/a bad Termix JWT: sync runs as a
|
||||
// plain server-to-server fetch() in this main process, with no browser
|
||||
// cookie jar at all, so re-authenticating through the login iframe (which
|
||||
// only affects the renderer's browser session) can never fix this --
|
||||
// reconnecting would tell the user to do something that doesn't help.
|
||||
// The proxy has to allow this traffic through some other way (an API
|
||||
// bypass rule, a separate hostname/port that isn't proxy-gated, etc.),
|
||||
// so this gets its own distinct, honest error rather than piggybacking
|
||||
// on needsReauth or a raw JSON.parse crash.
|
||||
const looksLikeHtml =
|
||||
text.includes("<html") ||
|
||||
text.includes("<!DOCTYPE") ||
|
||||
text.includes("<head>") ||
|
||||
text.includes("<body>");
|
||||
if (looksLikeHtml) {
|
||||
const err = new Error(
|
||||
"The reverse proxy in front of this server is blocking sync traffic with its own login page. Reconnecting won't fix this -- the proxy needs to let Termix's API requests through (e.g. an SSO bypass rule for the sync API, or a non-proxied hostname/port for it).",
|
||||
);
|
||||
err.proxyBlocked = true;
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
throw new Error(`Server returned invalid JSON: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
async pullSide(baseUrl, token, entityType, since) {
|
||||
const url = `${baseUrl}/sync/${entityType}${since ? `?since=${encodeURIComponent(since)}` : ""}`;
|
||||
const data = await this.fetchJson(url, token);
|
||||
return data.rows || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Every syncId a side currently holds, ignoring the incremental window.
|
||||
* Used only to decide whether a deletion still has something to delete.
|
||||
*/
|
||||
async pullSyncIds(baseUrl, token, entityType) {
|
||||
const rows = await this.pullSide(baseUrl, token, entityType, null);
|
||||
return new Set(rows.filter((row) => row.syncId).map((row) => row.syncId));
|
||||
}
|
||||
|
||||
async pullTombstones(baseUrl, token, entityType, since) {
|
||||
const url = `${baseUrl}/sync/${entityType}/tombstones${since ? `?since=${encodeURIComponent(since)}` : ""}`;
|
||||
const data = await this.fetchJson(url, token);
|
||||
return data.tombstones || [];
|
||||
}
|
||||
|
||||
async pushRow(baseUrl, token, entityType, row) {
|
||||
await this.fetchJson(`${baseUrl}/sync/${entityType}`, token, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ row }),
|
||||
});
|
||||
}
|
||||
|
||||
async pushTombstone(baseUrl, token, entityType, syncId) {
|
||||
await this.fetchJson(`${baseUrl}/sync/tombstones`, token, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ entityType, syncId }),
|
||||
});
|
||||
}
|
||||
|
||||
async syncEntity({ entityType, remoteBaseUrl, remoteJwt, since }) {
|
||||
const syncedAt = new Date().toISOString();
|
||||
try {
|
||||
const [localRows, remoteRows, localTombstones, remoteTombstones] =
|
||||
await Promise.all([
|
||||
this.pullSide(EMBEDDED_BASE_URL, this.localJwt, entityType, since),
|
||||
this.pullSide(remoteBaseUrl, remoteJwt, entityType, since),
|
||||
this.pullTombstones(
|
||||
EMBEDDED_BASE_URL,
|
||||
this.localJwt,
|
||||
entityType,
|
||||
since,
|
||||
),
|
||||
this.pullTombstones(remoteBaseUrl, remoteJwt, entityType, since),
|
||||
]);
|
||||
|
||||
const tombstonedSyncIds = new Set([
|
||||
...localTombstones.map((t) => t.syncId),
|
||||
...remoteTombstones.map((t) => t.syncId),
|
||||
]);
|
||||
|
||||
const localBySyncId = new Map(
|
||||
localRows.filter((r) => r.syncId).map((r) => [r.syncId, r]),
|
||||
);
|
||||
const remoteBySyncId = new Map(
|
||||
remoteRows.filter((r) => r.syncId).map((r) => [r.syncId, r]),
|
||||
);
|
||||
const allSyncIds = new Set([
|
||||
...localBySyncId.keys(),
|
||||
...remoteBySyncId.keys(),
|
||||
]);
|
||||
|
||||
for (const syncId of allSyncIds) {
|
||||
if (tombstonedSyncIds.has(syncId)) continue;
|
||||
|
||||
const localRow = localBySyncId.get(syncId);
|
||||
const remoteRow = remoteBySyncId.get(syncId);
|
||||
|
||||
if (localRow && !remoteRow) {
|
||||
await this.pushRow(remoteBaseUrl, remoteJwt, entityType, localRow);
|
||||
} else if (remoteRow && !localRow) {
|
||||
await this.pushRow(
|
||||
EMBEDDED_BASE_URL,
|
||||
this.localJwt,
|
||||
entityType,
|
||||
remoteRow,
|
||||
);
|
||||
} else if (localRow && remoteRow) {
|
||||
const localUpdatedAt = new Date(localRow.updatedAt || 0).getTime();
|
||||
const remoteUpdatedAt = new Date(remoteRow.updatedAt || 0).getTime();
|
||||
if (localUpdatedAt > remoteUpdatedAt) {
|
||||
await this.pushRow(remoteBaseUrl, remoteJwt, entityType, localRow);
|
||||
} else if (remoteUpdatedAt > localUpdatedAt) {
|
||||
await this.pushRow(
|
||||
EMBEDDED_BASE_URL,
|
||||
this.localJwt,
|
||||
entityType,
|
||||
remoteRow,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tombstones to whichever side hasn't already deleted the row.
|
||||
//
|
||||
// The presence check cannot use localRows/remoteRows: those are the
|
||||
// incremental window, and a row deleted on one side while untouched on
|
||||
// the other is by definition outside it, so every deletion was dropped.
|
||||
// It also cannot be skipped -- pushing unconditionally makes the
|
||||
// receiving side record a fresh tombstone, which the next pass would push
|
||||
// back, forever. So ask the receiving side what it actually still holds,
|
||||
// and only when there is a deletion to apply.
|
||||
if (localTombstones.length) {
|
||||
const remoteSyncIds = await this.pullSyncIds(
|
||||
remoteBaseUrl,
|
||||
remoteJwt,
|
||||
entityType,
|
||||
);
|
||||
for (const tombstone of localTombstones) {
|
||||
if (remoteSyncIds.has(tombstone.syncId)) {
|
||||
await this.pushTombstone(
|
||||
remoteBaseUrl,
|
||||
remoteJwt,
|
||||
entityType,
|
||||
tombstone.syncId,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remoteTombstones.length) {
|
||||
const localSyncIds = await this.pullSyncIds(
|
||||
EMBEDDED_BASE_URL,
|
||||
this.localJwt,
|
||||
entityType,
|
||||
);
|
||||
for (const tombstone of remoteTombstones) {
|
||||
if (localSyncIds.has(tombstone.syncId)) {
|
||||
await this.pushTombstone(
|
||||
EMBEDDED_BASE_URL,
|
||||
this.localJwt,
|
||||
entityType,
|
||||
tombstone.syncId,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { syncedAt };
|
||||
} catch (error) {
|
||||
if (error?.authFailure) {
|
||||
return { syncedAt, authFailure: true };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let engine = null;
|
||||
|
||||
function initRemoteSync(getMainWindow) {
|
||||
engine = new RemoteSyncEngine(getMainWindow);
|
||||
engine.start();
|
||||
return engine;
|
||||
}
|
||||
|
||||
function getRemoteSyncEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initRemoteSync,
|
||||
getRemoteSyncEngine,
|
||||
getDesktopSettings,
|
||||
saveDesktopSettings,
|
||||
getRemoteSyncConfig,
|
||||
saveRemoteSyncConfig,
|
||||
clearRemoteSyncConfig,
|
||||
saveRemoteSyncJwt,
|
||||
getRemoteSyncJwt,
|
||||
clearRemoteSyncJwt,
|
||||
getRemoteSyncUserInfo,
|
||||
isJwtExpiredOrExpiringSoon,
|
||||
decodeJwtExpiry,
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
import reactRefresh from "eslint-plugin-react-refresh";
|
||||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
import tseslint from "typescript-eslint";
|
||||
import { globalIgnores } from "eslint/config";
|
||||
|
||||
export default tseslint.config([
|
||||
globalIgnores(["dist", "release", "Mobile"]),
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
plugins: {
|
||||
"react-hooks": reactHooks,
|
||||
"unused-imports": unusedImports,
|
||||
},
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
vars: "all",
|
||||
varsIgnorePattern: "^_",
|
||||
args: "after-used",
|
||||
argsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-expressions": "warn",
|
||||
"no-empty": "warn",
|
||||
"no-control-regex": "off",
|
||||
"no-useless-assignment": "off",
|
||||
"preserve-caught-error": "off",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-refresh/only-export-components": "warn",
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -0,0 +1,102 @@
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import reactHooks from "eslint-plugin-react-hooks";
|
||||
import reactRefresh from "eslint-plugin-react-refresh";
|
||||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
import tseslint from "typescript-eslint";
|
||||
import { globalIgnores } from "eslint/config";
|
||||
|
||||
export default tseslint.config([
|
||||
globalIgnores(["dist", "release", "Mobile", "src/mcp-server/node_modules"]),
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
plugins: {
|
||||
"react-hooks": reactHooks,
|
||||
"unused-imports": unusedImports,
|
||||
},
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
vars: "all",
|
||||
varsIgnorePattern: "^_",
|
||||
args: "after-used",
|
||||
argsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-expressions": "warn",
|
||||
"no-empty": "warn",
|
||||
"no-control-regex": "off",
|
||||
"no-useless-assignment": "off",
|
||||
"preserve-caught-error": "off",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-refresh/only-export-components": "warn",
|
||||
},
|
||||
},
|
||||
{
|
||||
// MySQL has no RETURNING clause, and drizzle's mysql-core does not expose
|
||||
// the method at all — a bare .returning() is a TypeError there, not a bad
|
||||
// query, and it only fails on the engine no test in this repo runs against.
|
||||
//
|
||||
// 175 call sites were migrated off it. This is what stops number 176.
|
||||
// Writes that need rows back go through repositories/returning.ts, which
|
||||
// picks one statement or a read-then-write transaction per dialect.
|
||||
files: ["src/backend/database/repositories/**/*.ts"],
|
||||
ignores: [
|
||||
// The two files whose job is to absorb these differences.
|
||||
"src/backend/database/repositories/returning.ts",
|
||||
"src/backend/database/repositories/mutation-result.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.property.name='returning']",
|
||||
message:
|
||||
"MySQL has no RETURNING. Use insertReturning/updateReturning/deleteReturning from ./returning.js, or rowsAffected() if you only need a count. Inside a proven sqlite-only branch, disable this rule with a comment saying so.",
|
||||
},
|
||||
{
|
||||
// `||` concatenates on SQLite and Postgres. On MySQL it is logical OR
|
||||
// unless the server runs with PIPES_AS_CONCAT, so a folder path built
|
||||
// this way silently became 0. Use CONCAT, which all three agree on.
|
||||
selector:
|
||||
"TaggedTemplateExpression[tag.name='sql'] TemplateElement[value.raw=/\\|\\|/]",
|
||||
message:
|
||||
"`||` is logical OR on MySQL, not concatenation. Use CONCAT(...).",
|
||||
},
|
||||
{
|
||||
// Postgres and SQLite spell it ON CONFLICT; MySQL spells it ON
|
||||
// DUPLICATE KEY and names no columns, so drizzle's mysql-core has no
|
||||
// onConflictDoUpdate at all — another TypeError, not a bad query.
|
||||
selector: "CallExpression[callee.property.name='onConflictDoUpdate']",
|
||||
message:
|
||||
"MySQL has no ON CONFLICT. Use upsert() from ./returning.js.",
|
||||
},
|
||||
{
|
||||
// better-sqlite3 puts these on a write result; node-postgres and
|
||||
// mysql2 do not, so reading them directly yields undefined — and
|
||||
// Number(undefined) is NaN, which reaches the database as the string
|
||||
// "NaN" and fails an integer column. Three call sites did exactly
|
||||
// this and only broke on Postgres.
|
||||
selector:
|
||||
"MemberExpression[property.name=/^(lastInsertRowid|changes)$/]",
|
||||
message:
|
||||
"lastInsertRowid and changes are better-sqlite3 only. Use insertedId() or rowsAffected() from ./mutation-result.js.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -1,11 +0,0 @@
|
||||
[Flatpak Ref]
|
||||
Name=Termix
|
||||
Branch=stable
|
||||
Title=Termix - SSH Server Management Platform
|
||||
IsRuntime=false
|
||||
Url=https://github.com/Termix-SSH/Termix/releases/download/VERSION_PLACEHOLDER/termix_linux_flatpak.flatpak
|
||||
RuntimeRepo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
Comment=Web-based server management platform with SSH terminal, tunneling, and file editing
|
||||
Description=Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides SSH terminal access, tunneling capabilities, and remote file management.
|
||||
Icon=https://raw.githubusercontent.com/Termix-SSH/Termix/main/public/icon.png
|
||||
Homepage=https://github.com/Termix-SSH/Termix
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.karmaa.termix</id>
|
||||
<name>Termix</name>
|
||||
<summary>Web-based server management platform with SSH terminal, tunneling, and file editing</summary>
|
||||
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>Apache-2.0</project_license>
|
||||
|
||||
<developer_name>bugattiguy527</developer_name>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform.
|
||||
It provides a web-based solution for managing your servers and infrastructure through a single, intuitive interface.
|
||||
</p>
|
||||
<p>Features:</p>
|
||||
<ul>
|
||||
<li>SSH terminal access with full terminal emulation</li>
|
||||
<li>SSH tunneling capabilities for secure port forwarding</li>
|
||||
<li>Remote file management with editor support</li>
|
||||
<li>Server monitoring and management tools</li>
|
||||
<li>Self-hosted solution - keep your data private</li>
|
||||
<li>Modern, intuitive web interface</li>
|
||||
</ul>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">com.karmaa.termix.desktop</launchable>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/Termix-SSH/Termix/main/public/screenshots/terminal.png</image>
|
||||
<caption>SSH Terminal Interface</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<url type="homepage">https://github.com/Termix-SSH/Termix</url>
|
||||
<url type="bugtracker">https://github.com/Termix-SSH/Support/issues</url>
|
||||
<url type="help">https://docs.termix.site</url>
|
||||
<url type="vcs-browser">https://github.com/Termix-SSH/Termix</url>
|
||||
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="social-info">moderate</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release version="VERSION_PLACEHOLDER" date="DATE_PLACEHOLDER">
|
||||
<description>
|
||||
<p>Latest release of Termix</p>
|
||||
</description>
|
||||
<url>https://github.com/Termix-SSH/Termix/releases</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
<categories>
|
||||
<category>Development</category>
|
||||
<category>Network</category>
|
||||
<category>System</category>
|
||||
</categories>
|
||||
|
||||
<keywords>
|
||||
<keyword>ssh</keyword>
|
||||
<keyword>terminal</keyword>
|
||||
<keyword>server</keyword>
|
||||
<keyword>management</keyword>
|
||||
<keyword>tunnel</keyword>
|
||||
<keyword>file-manager</keyword>
|
||||
</keywords>
|
||||
|
||||
<provides>
|
||||
<binary>termix</binary>
|
||||
</provides>
|
||||
|
||||
<requires>
|
||||
<internet>always</internet>
|
||||
</requires>
|
||||
</component>
|
||||
@@ -2,8 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||
/>
|
||||
|
||||
<meta name="theme-color" content="#09090b" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
@@ -12,8 +15,8 @@
|
||||
content="black-translucent"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-title" content="Termix" />
|
||||
<link rel="apple-touch-icon" href="/icons/512x512.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="apple-touch-icon" href="icons/512x512.png" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<title>Termix</title>
|
||||
<style>
|
||||
.hide-scrollbar {
|
||||
@@ -47,6 +50,9 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.__TERMIX_BASE_PATH__ = "";
|
||||
</script>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@latest/schema.json",
|
||||
"entry": [
|
||||
"src/backend/starter.ts",
|
||||
"src/backend/swagger.ts",
|
||||
"scripts/**/*.cjs"
|
||||
],
|
||||
"project": ["src/**/*.{ts,tsx}", "electron/**/*.cjs", "scripts/**/*.cjs"],
|
||||
"ignoreBinaries": ["powershell"],
|
||||
"ignoreDependencies": [
|
||||
"@codemirror/autocomplete",
|
||||
"@codemirror/commands",
|
||||
"@codemirror/search",
|
||||
"@codemirror/theme-one-dark",
|
||||
"@codemirror/view",
|
||||
"@electron/notarize",
|
||||
"@monaco-editor/react",
|
||||
"@radix-ui/react-accordion",
|
||||
"@radix-ui/react-alert-dialog",
|
||||
"@radix-ui/react-checkbox",
|
||||
"@radix-ui/react-dialog",
|
||||
"@radix-ui/react-dropdown-menu",
|
||||
"@radix-ui/react-label",
|
||||
"@radix-ui/react-popover",
|
||||
"@radix-ui/react-progress",
|
||||
"@radix-ui/react-scroll-area",
|
||||
"@radix-ui/react-select",
|
||||
"@radix-ui/react-separator",
|
||||
"@radix-ui/react-slider",
|
||||
"@radix-ui/react-slot",
|
||||
"@radix-ui/react-switch",
|
||||
"@radix-ui/react-tabs",
|
||||
"@radix-ui/react-tooltip",
|
||||
"@types/better-sqlite3",
|
||||
"@types/cookie-parser",
|
||||
"@types/cors",
|
||||
"@types/express",
|
||||
"@types/guacamole-common-js",
|
||||
"@types/js-yaml",
|
||||
"@types/jsonwebtoken",
|
||||
"@types/multer",
|
||||
"@types/qrcode",
|
||||
"@types/speakeasy",
|
||||
"@types/ssh2",
|
||||
"@uiw/codemirror-extensions-langs",
|
||||
"@uiw/codemirror-theme-github",
|
||||
"@uiw/react-codemirror",
|
||||
"@xterm/addon-clipboard",
|
||||
"@xterm/addon-fit",
|
||||
"@xterm/addon-unicode11",
|
||||
"@xterm/addon-web-links",
|
||||
"@xterm/xterm",
|
||||
"axios",
|
||||
"bcryptjs",
|
||||
"better-sqlite3",
|
||||
"body-parser",
|
||||
"chalk",
|
||||
"class-variance-authority",
|
||||
"clsx",
|
||||
"cmdk",
|
||||
"cookie-parser",
|
||||
"cors",
|
||||
"cytoscape",
|
||||
"drizzle-orm",
|
||||
"express",
|
||||
"guacamole-lite",
|
||||
"guacamole-common-js",
|
||||
"jose",
|
||||
"js-yaml",
|
||||
"jsonwebtoken",
|
||||
"jszip",
|
||||
"multer",
|
||||
"nanoid",
|
||||
"qrcode",
|
||||
"i18next-browser-languagedetector",
|
||||
"lucide-react",
|
||||
"motion",
|
||||
"radix-ui",
|
||||
"react-cytoscapejs",
|
||||
"react-h5-audio-player",
|
||||
"react-hook-form",
|
||||
"react-icons",
|
||||
"react-markdown",
|
||||
"react-pdf",
|
||||
"react-photo-view",
|
||||
"react-syntax-highlighter",
|
||||
"react-xtermjs",
|
||||
"remark-gfm",
|
||||
"socks",
|
||||
"speakeasy",
|
||||
"ssh2",
|
||||
"undici",
|
||||
"husky",
|
||||
"sharp",
|
||||
"sonner",
|
||||
"tailwind-merge"
|
||||
]
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
{
|
||||
"name": "termix",
|
||||
"private": true,
|
||||
"version": "2.2.1",
|
||||
"description": "A web-based server management platform with SSH terminal, tunneling, and file editing capabilities",
|
||||
"version": "2.6.1",
|
||||
"description": "Self-hosted SSH and remote desktop management.",
|
||||
"author": "Karmaa",
|
||||
"main": "electron/main.cjs",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.12.0",
|
||||
"npm": ">=11"
|
||||
@@ -13,89 +12,113 @@
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"postinstall": "node scripts/patch-app-builder-lib.cjs",
|
||||
"biome:check": "biome check biome.json package.json",
|
||||
"biome:fix": "biome check --write biome.json package.json",
|
||||
"postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-guacamole-common-js.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-xterm-android-ime.cjs",
|
||||
"prebuild": "node scripts/write-electron-build-info.cjs",
|
||||
"lint": "eslint .",
|
||||
"lint": "node scripts/generate-dialect-schema.cjs --check && eslint .",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"type-check": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"verify:dialect": "tsx scripts/verify-dialects.mjs",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"dev": "vite",
|
||||
"build": "vite build && tsc -p tsconfig.node.json",
|
||||
"build:backend": "tsc -p tsconfig.node.json",
|
||||
"dev:backend": "tsc -p tsconfig.node.json && node ./dist/backend/backend/starter.js",
|
||||
"build": "vite build && tsc -p tsconfig.node.json && node -e \"require('fs').copyFileSync('src/backend/package.json','dist/backend/package.json')\"",
|
||||
"build:backend": "tsc -p tsconfig.node.json && node -e \"require('fs').copyFileSync('src/backend/package.json','dist/backend/package.json')\"",
|
||||
"dev:backend": "tsc -p tsconfig.node.json && node -e \"require('fs').copyFileSync('src/backend/package.json','dist/backend/package.json')\" && node ./dist/backend/backend/starter.js",
|
||||
"dev:docker": "docker stop termix-dev 2>nul & docker rm termix-dev 2>nul & docker build -f docker/Dockerfile -t termix:dev --no-cache . && docker run -d --name termix-dev -p 3000:3000 -p 8080:8080 -p 30001-30006:30001-30006 -v \"%cd%\\db\\data:/app/data\" termix:dev",
|
||||
"dev:docker:restart": "docker stop termix-dev 2>nul & docker rm termix-dev 2>nul & docker run -d --name termix-dev -p 8080:8080 -p 30001-30006:30001-30006 -v \"%cd%\\db\\data:/app/data\" termix:dev",
|
||||
"generate:openapi": "tsc -p tsconfig.node.json && node ./dist/backend/backend/swagger.js",
|
||||
"generate:openapi": "tsc -p tsconfig.node.json && node -e \"require('fs').copyFileSync('src/backend/package.json','dist/backend/package.json')\" && node ./dist/backend/backend/utils/swagger.js",
|
||||
"preview": "vite preview",
|
||||
"electron:dev": "concurrently \"npm run dev\" \"powershell -c \\\"Start-Sleep -Seconds 5\\\" && electron .\"",
|
||||
"electron:patch-builder": "node scripts/patch-app-builder-lib.cjs",
|
||||
"electron:rebuild": "electron-rebuild -f -w better-sqlite3",
|
||||
"electron:rebuild": "electron-rebuild -f -w better-sqlite3 -w serialport",
|
||||
"build:win-portable": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --win --dir",
|
||||
"build:win-installer": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --win --publish=never",
|
||||
"build:linux-portable": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --linux --dir",
|
||||
"build:linux-appimage": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --linux AppImage",
|
||||
"build:linux-targz": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --linux tar.gz",
|
||||
"build:mac": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --mac --universal",
|
||||
"build:mac-dev": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --mac dir --publish=never"
|
||||
"build:mac-dev": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --mac dir --publish=never",
|
||||
"schema:generate": "node scripts/generate-dialect-schema.cjs",
|
||||
"schema:check": "node scripts/generate-dialect-schema.cjs --check",
|
||||
"schema:migrations": "drizzle-kit generate --config=drizzle.config.sqlite.ts && drizzle-kit generate --config=drizzle.config.pg.ts && drizzle-kit generate --config=drizzle.config.mysql.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.15.2",
|
||||
"@simplewebauthn/browser": "^13.3.0",
|
||||
"@simplewebauthn/server": "^13.3.2",
|
||||
"@tanstack/react-virtual": "^3.14.9",
|
||||
"@types/ldapjs": "^3.0.6",
|
||||
"axios": "^1.19.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"better-sqlite3": "^12.9.0",
|
||||
"body-parser": "^2.2.2",
|
||||
"chalk": "^5.6.2",
|
||||
"better-sqlite3": "^13.0.2",
|
||||
"body-parser": "^2.3.0",
|
||||
"chalk": "^6.0.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"express": "^5.2.1",
|
||||
"guacamole-lite": "^1.2.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"jose": "^6.2.2",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jose": "^6.2.5",
|
||||
"js-yaml": "^5.2.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"jszip": "^3.10.1",
|
||||
"motion": "^12.38.0",
|
||||
"multer": "^2.1.1",
|
||||
"nanoid": "^5.1.9",
|
||||
"ldapjs": "^3.0.7",
|
||||
"motion": "^12.43.0",
|
||||
"multer": "^2.2.0",
|
||||
"mysql2": "^3.23.2",
|
||||
"nanoid": "^6.0.0",
|
||||
"pg": "^8.22.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react-is": "^19.2.5",
|
||||
"serialport": "^13.0.0",
|
||||
"socks": "^2.8.7",
|
||||
"speakeasy": "^2.0.0",
|
||||
"ssh2": "^1.17.0",
|
||||
"undici": "^7.0.0",
|
||||
"ws": "^8.20.0"
|
||||
"undici": "^8.9.0",
|
||||
"ws": "^8.21.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/autocomplete": "^6.20.1",
|
||||
"@codemirror/commands": "^6.10.3",
|
||||
"@codemirror/search": "^6.7.0",
|
||||
"@biomejs/biome": "2.5.6",
|
||||
"@codemirror/autocomplete": "^6.20.3",
|
||||
"@codemirror/commands": "^6.10.4",
|
||||
"@codemirror/search": "^6.7.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@codemirror/view": "^6.41.1",
|
||||
"@commitlint/cli": "^20.5.0",
|
||||
"@commitlint/config-conventional": "^20.5.0",
|
||||
"@codemirror/view": "^6.43.7",
|
||||
"@commitlint/cli": "^21.2.1",
|
||||
"@commitlint/config-conventional": "^21.2.0",
|
||||
"@deadendjs/swagger-jsdoc": "^8.1.2",
|
||||
"@electron/notarize": "^3.1.1",
|
||||
"@electron/rebuild": "^4.0.4",
|
||||
"@eslint/js": "^9.0.0",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@electron/rebuild": "^4.2.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
||||
"@fontsource/fira-code": "^5.3.0",
|
||||
"@fontsource/jetbrains-mono": "^5.3.0",
|
||||
"@fontsource/source-code-pro": "^5.3.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
"@radix-ui/react-accordion": "^1.2.20",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.23",
|
||||
"@radix-ui/react-checkbox": "^1.3.11",
|
||||
"@radix-ui/react-dialog": "^1.1.23",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.24",
|
||||
"@radix-ui/react-label": "^2.1.15",
|
||||
"@radix-ui/react-popover": "^1.1.23",
|
||||
"@radix-ui/react-progress": "^1.1.16",
|
||||
"@radix-ui/react-scroll-area": "^1.2.18",
|
||||
"@radix-ui/react-select": "^2.3.7",
|
||||
"@radix-ui/react-separator": "^1.1.15",
|
||||
"@radix-ui/react-slider": "^1.4.7",
|
||||
"@radix-ui/react-slot": "^1.3.3",
|
||||
"@radix-ui/react-switch": "^1.3.7",
|
||||
"@radix-ui/react-tabs": "^1.1.21",
|
||||
"@radix-ui/react-tooltip": "^1.2.16",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^7.0.0",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
@@ -103,67 +126,72 @@
|
||||
"@types/guacamole-common-js": "^1.5.5",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/multer": "^2.1.0",
|
||||
"@types/node": "^24.12.2",
|
||||
"@types/multer": "^2.2.0",
|
||||
"@types/node": "^26.1.2",
|
||||
"@types/pg": "^8.20.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@types/speakeasy": "^2.0.10",
|
||||
"@types/ssh2": "^1.15.5",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@uiw/codemirror-extensions-langs": "^4.25.9",
|
||||
"@uiw/codemirror-theme-github": "^4.25.9",
|
||||
"@uiw/react-codemirror": "^4.25.9",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"@uiw/codemirror-extensions-langs": "^4.25.11",
|
||||
"@uiw/codemirror-theme-github": "^4.25.11",
|
||||
"@uiw/react-codemirror": "^4.25.11",
|
||||
"@vitejs/plugin-react": "^6.0.5",
|
||||
"@vitest/coverage-v8": "^4.1.10",
|
||||
"@vitest/ui": "^4.1.10",
|
||||
"@xterm/addon-clipboard": "^0.2.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-unicode11": "^0.8.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-search": "^0.16.0",
|
||||
"@xterm/addon-unicode11": "^0.9.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"cytoscape": "^3.33.2",
|
||||
"electron": "^41.3.0",
|
||||
"electron-builder": "^26.8.1",
|
||||
"eslint": "^9.0.0",
|
||||
"concurrently": "^10.0.4",
|
||||
"cytoscape": "^3.34.0",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"electron": "^43.2.0",
|
||||
"electron-builder": "^26.15.3",
|
||||
"eslint": "^10.8.0",
|
||||
"eslint-plugin-react-hooks": "^7.1.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.2",
|
||||
"eslint-plugin-react-refresh": "^0.5.3",
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"globals": "^17.5.0",
|
||||
"globals": "^17.8.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"husky": "^9.1.7",
|
||||
"i18next": "^26.0.8",
|
||||
"i18next": "^26.3.6",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"lint-staged": "^16.4.0",
|
||||
"lucide-react": "^1.11.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"prettier": "3.8.3",
|
||||
"react": "^19.2.5",
|
||||
"jsdom": "^29.1.1",
|
||||
"lint-staged": "^17.2.0",
|
||||
"lucide-react": "^1.28.0",
|
||||
"prettier": "3.9.6",
|
||||
"radix-ui": "^1.6.7",
|
||||
"react": "^19.2.8",
|
||||
"react-cytoscapejs": "^2.0.0",
|
||||
"react-dom": "^19.2.5",
|
||||
"react-grid-layout": "^2.2.3",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-h5-audio-player": "^3.10.2",
|
||||
"react-hook-form": "^7.73.1",
|
||||
"react-i18next": "^17.0.4",
|
||||
"react-hook-form": "^7.79.0",
|
||||
"react-i18next": "^17.0.11",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-pdf": "^10.4.1",
|
||||
"react-photo-view": "^1.2.7",
|
||||
"react-resizable-panels": "^4.10.0",
|
||||
"react-simple-keyboard": "^3.8.196",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
"react-xtermjs": "^1.0.10",
|
||||
"recharts": "^3.8.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sharp": "^0.35.3",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.4",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.59.0",
|
||||
"vite": "^8.0.10",
|
||||
"zod": "^4.3.6"
|
||||
"typescript-eslint": "^8.61.1",
|
||||
"vite": "^8.0.16",
|
||||
"vite-plugin-svgr": "^5.2.0",
|
||||
"vitest": "^4.1.10"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
exports.default = async function afterPack(context) {
|
||||
const { targets, appOutDir } = context;
|
||||
|
||||
const isDir = targets.some((t) => t.name === "dir");
|
||||
if (!isDir) return;
|
||||
|
||||
const markerPath = path.join(appOutDir, ".portable");
|
||||
fs.writeFileSync(markerPath, "");
|
||||
};
|
||||
@@ -14,16 +14,10 @@
|
||||
<projectSourceUrl>https://github.com/Termix-SSH/Termix</projectSourceUrl>
|
||||
<docsUrl>https://docs.termix.site/install</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/Termix-SSH/Support/issues</bugTrackerUrl>
|
||||
<tags>docker ssh self-hosted file-management ssh-tunnel termix server-management terminal</tags>
|
||||
<summary>Termix is a web-based server management platform with SSH terminal, tunneling, and file editing capabilities.</summary>
|
||||
<tags>docker ssh terminal telnet self-hosted rdp file-management vnc ssh-tunnel server-stats termix</tags>
|
||||
<summary>Self-hosted SSH and remote desktop management.</summary>
|
||||
<description>
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a web-based solution for managing your servers and infrastructure through a single, intuitive interface.
|
||||
|
||||
Termix offers:
|
||||
- SSH terminal access
|
||||
- SSH tunneling capabilities
|
||||
- Remote file management
|
||||
- Server monitoring and management
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a multi-platform solution for managing your servers and infrastructure through a single, intuitive interface. Termix offers SSH terminal access, remote desktop control (RDP, VNC, Telnet), SSH tunneling capabilities, remote file management, and many other tools. Termix is the perfect free and self-hosted alternative to Termius available for all platforms.
|
||||
|
||||
This package installs the desktop application version of Termix.
|
||||
</description>
|
||||
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Name=Termix
|
||||
Comment=Web-based server management platform with SSH terminal, tunneling, and file editing
|
||||
Comment=Self-hosted SSH and remote desktop management.
|
||||
Exec=run.sh %U
|
||||
Icon=com.karmaa.termix
|
||||
Terminal=false
|
||||
@@ -0,0 +1,11 @@
|
||||
[Flatpak Ref]
|
||||
Name=Termix
|
||||
Branch=stable
|
||||
Title=Termix - SSH Server Management Platform
|
||||
IsRuntime=false
|
||||
Url=https://github.com/Termix-SSH/Termix/releases/download/VERSION_PLACEHOLDER/termix_linux_flatpak.flatpak
|
||||
RuntimeRepo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
Comment=Self-hosted SSH and remote desktop management.
|
||||
Description=Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a multi-platform solution for managing your servers and infrastructure through a single, intuitive interface. Termix offers SSH terminal access, remote desktop control (RDP, VNC, Telnet), SSH tunneling capabilities, remote file management, and many other tools. Termix is the perfect free and self-hosted alternative to Termius available for all platforms.
|
||||
Icon=https://raw.githubusercontent.com/Termix-SSH/Termix/main/public/icon.png
|
||||
Homepage=https://github.com/Termix-SSH/Termix
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.karmaa.termix</id>
|
||||
<name>Termix</name>
|
||||
<summary>Self-hosted SSH and remote desktop management.</summary>
|
||||
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>Apache-2.0</project_license>
|
||||
|
||||
<developer_name>bugattiguy527</developer_name>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides a
|
||||
multi-platform solution for managing your servers and infrastructure through a single, intuitive interface.
|
||||
Termix offers SSH terminal access, remote desktop control (RDP, VNC, Telnet), SSH tunneling capabilities,
|
||||
remote file management, and many other tools. Termix is the perfect free and self-hosted alternative to
|
||||
Termius available for all platforms.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">com.karmaa.termix.desktop</launchable>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/Termix-SSH/Termix/main/public/screenshots/terminal.png</image>
|
||||
<caption>SSH Terminal Interface</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<url type="homepage">https://github.com/Termix-SSH/Termix</url>
|
||||
<url type="bugtracker">https://github.com/Termix-SSH/Support/issues</url>
|
||||
<url type="help">https://docs.termix.site</url>
|
||||
<url type="vcs-browser">https://github.com/Termix-SSH/Termix</url>
|
||||
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="social-info">moderate</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release version="VERSION_PLACEHOLDER" date="DATE_PLACEHOLDER">
|
||||
<description>
|
||||
<p>Latest release of Termix</p>
|
||||
</description>
|
||||
<url>https://github.com/Termix-SSH/Termix/releases</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
<categories>
|
||||
<category>Development</category>
|
||||
<category>Network</category>
|
||||
<category>System</category>
|
||||
</categories>
|
||||
|
||||
<keywords>
|
||||
<keyword>docker</keyword>
|
||||
<keyword>ssh</keyword>
|
||||
<keyword>terminal</keyword>
|
||||
<keyword>telnet</keyword>
|
||||
<keyword>self-hosted</keyword>
|
||||
<keyword>rdp</keyword>
|
||||
<keyword>file-management</keyword>
|
||||
<keyword>vnc</keyword>
|
||||
<keyword>ssh-tunnel</keyword>
|
||||
<keyword>server-stats</keyword>
|
||||
<keyword>termix</keyword>
|
||||
|
||||
</keywords>
|
||||
|
||||
<provides>
|
||||
<binary>termix</binary>
|
||||
</provides>
|
||||
|
||||
<requires>
|
||||
<internet>always</internet>
|
||||
</requires>
|
||||
</component>
|
||||