Compare commits

..

No commits in common. "develop" and "release/0.1.4" have entirely different histories.

640 changed files with 11207 additions and 37397 deletions

View file

@ -1,413 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "meienberger",
"name": "Nicolas Meienberger",
"avatar_url": "https://avatars.githubusercontent.com/u/47644445?v=4",
"profile": "https://meienberger.dev/",
"contributions": [
"code",
"infra",
"test",
"doc"
]
},
{
"login": "ArneNaessens",
"name": "ArneNaessens",
"avatar_url": "https://avatars.githubusercontent.com/u/16622722?v=4",
"profile": "https://github.com/ArneNaessens",
"contributions": [
"code",
"ideas",
"test"
]
},
{
"login": "DrMxrcy",
"name": "DrMxrcy",
"avatar_url": "https://avatars.githubusercontent.com/u/58747968?v=4",
"profile": "https://github.com/DrMxrcy",
"contributions": [
"code",
"ideas",
"test",
"content",
"promotion",
"question",
"review"
]
},
{
"login": "CobreDev",
"name": "Cooper",
"avatar_url": "https://avatars.githubusercontent.com/u/36574329?v=4",
"profile": "https://cobre.dev",
"contributions": [
"code"
]
},
{
"login": "JTruj1ll0923",
"name": "JTruj1ll0923",
"avatar_url": "https://avatars.githubusercontent.com/u/6656643?v=4",
"profile": "https://github.com/JTruj1ll0923",
"contributions": [
"code"
]
},
{
"login": "Stetsed",
"name": "Stetsed",
"avatar_url": "https://avatars.githubusercontent.com/u/33891782?v=4",
"profile": "https://github.com/Stetsed",
"contributions": [
"code"
]
},
{
"login": "blushell",
"name": "Jones_Town",
"avatar_url": "https://avatars.githubusercontent.com/u/3621606?v=4",
"profile": "https://github.com/blushell",
"contributions": [
"code"
]
},
{
"login": "rushic24",
"name": "Rushi Chaudhari",
"avatar_url": "https://avatars.githubusercontent.com/u/6279035?v=4",
"profile": "https://rushichaudhari.github.io/",
"contributions": [
"code"
]
},
{
"login": "rblaine95",
"name": "Robert Blaine",
"avatar_url": "https://avatars.githubusercontent.com/u/4052340?v=4",
"profile": "https://github.com/rblaine95",
"contributions": [
"code"
]
},
{
"login": "sethforprivacy",
"name": "Seth For Privacy",
"avatar_url": "https://avatars.githubusercontent.com/u/40500387?v=4",
"profile": "https://sethforprivacy.com",
"contributions": [
"code"
]
},
{
"login": "hqwuzhaoyi",
"name": "Prajna",
"avatar_url": "https://avatars.githubusercontent.com/u/44605072?v=4",
"profile": "https://github.com/hqwuzhaoyi",
"contributions": [
"code"
]
},
{
"login": "justincmoy",
"name": "Justin Moy",
"avatar_url": "https://avatars.githubusercontent.com/u/14875982?v=4",
"profile": "https://github.com/justincmoy",
"contributions": [
"code"
]
},
{
"login": "dextreem",
"name": "dextreem",
"avatar_url": "https://avatars.githubusercontent.com/u/11060652?v=4",
"profile": "https://github.com/dextreem",
"contributions": [
"code"
]
},
{
"login": "iBicha",
"name": "Brahim Hadriche",
"avatar_url": "https://avatars.githubusercontent.com/u/17722782?v=4",
"profile": "https://github.com/iBicha",
"contributions": [
"code"
]
},
{
"login": "andrewbrereton",
"name": "Andrew Brereton",
"avatar_url": "https://avatars.githubusercontent.com/u/682893?v=4",
"profile": "https://andrewbrereton.com",
"contributions": [
"content"
]
},
{
"login": "fsackur",
"name": "Freddie Sackur",
"avatar_url": "https://avatars.githubusercontent.com/u/3678789?v=4",
"profile": "https://fsackur.github.io/",
"contributions": [
"code",
"doc"
]
},
{
"login": "innocentius",
"name": "Innocentius",
"avatar_url": "https://avatars.githubusercontent.com/u/5344432?v=4",
"profile": "http://innocentius.github.io",
"contributions": [
"translation"
]
},
{
"login": "TetrisIQ",
"name": "Alex",
"avatar_url": "https://avatars.githubusercontent.com/u/24246993?v=4",
"profile": "https://github.com/TetrisIQ",
"contributions": [
"code"
]
},
{
"login": "ruibaby",
"name": "Ryan Wang",
"avatar_url": "https://avatars.githubusercontent.com/u/21301288?v=4",
"profile": "https://ryanc.cc",
"contributions": [
"code"
]
},
{
"login": "simonandr",
"name": "simonandr",
"avatar_url": "https://avatars.githubusercontent.com/u/48092304?v=4",
"profile": "https://github.com/simonandr",
"contributions": [
"content"
]
},
{
"login": "demizeu",
"name": "iepure",
"avatar_url": "https://avatars.githubusercontent.com/u/121183951?v=4",
"profile": "https://github.com/demizeu",
"contributions": [
"translation"
]
},
{
"login": "SergeyKodolov",
"name": "Sergey Kodolov",
"avatar_url": "https://avatars.githubusercontent.com/u/35339452?v=4",
"profile": "https://github.com/SergeyKodolov",
"contributions": [
"translation",
"code"
]
},
{
"login": "sclaren",
"name": "sclaren",
"avatar_url": "https://avatars.githubusercontent.com/u/915292?v=4",
"profile": "https://github.com/sclaren",
"contributions": [
"code"
]
},
{
"login": "mcmeel",
"name": "mcmeel",
"avatar_url": "https://avatars.githubusercontent.com/u/13773536?v=4",
"profile": "https://github.com/mcmeel",
"contributions": [
"question",
"ideas",
"code",
"doc"
]
},
{
"login": "NoisyFridge",
"name": "NoisyFridge",
"avatar_url": "https://avatars.githubusercontent.com/u/73795785?v=4",
"profile": "https://github.com/NoisyFridge",
"contributions": [
"translation"
]
},
{
"login": "Bvoxl",
"name": "Bvoxl",
"avatar_url": "https://avatars.githubusercontent.com/u/67489519?v=4",
"profile": "https://github.com/Bvoxl",
"contributions": [
"translation"
]
},
{
"login": "m-lab-0",
"name": "m-lab-0",
"avatar_url": "https://avatars.githubusercontent.com/u/116570617?v=4",
"profile": "https://github.com/m-lab-0",
"contributions": [
"translation"
]
},
{
"login": "dannkunt",
"name": "dannkunt",
"avatar_url": "https://avatars.githubusercontent.com/u/32395839?v=4",
"profile": "https://github.com/dannkunt",
"contributions": [
"translation"
]
},
{
"login": "Schmanko",
"name": "Schmanko",
"avatar_url": "https://avatars.githubusercontent.com/u/94195393?v=4",
"profile": "https://github.com/Schmanko",
"contributions": [
"translation"
]
},
{
"login": "nghialele",
"name": "Nghia Lele",
"avatar_url": "https://avatars.githubusercontent.com/u/129353223?v=4",
"profile": "https://micro.nghialele.com",
"contributions": [
"translation"
]
},
{
"login": "amusingimpala75",
"name": "amusingimpala75",
"avatar_url": "https://avatars.githubusercontent.com/u/69653100?v=4",
"profile": "https://github.com/amusingimpala75",
"contributions": [
"code"
]
},
{
"login": "M1n-4d316e",
"name": "David",
"avatar_url": "https://avatars.githubusercontent.com/u/54779580?v=4",
"profile": "http://m1n.omg.lol",
"contributions": [
"translation"
]
},
{
"login": "steveiliop56",
"name": "Stavros Iliopoulos",
"avatar_url": "https://avatars.githubusercontent.com/u/106091011?v=4",
"profile": "https://github.com/steveiliop56",
"contributions": [
"translation",
"code",
"test"
]
},
{
"login": "loxiry",
"name": "loxiry",
"avatar_url": "https://avatars.githubusercontent.com/u/86959495?v=4",
"profile": "https://github.com/loxiry",
"contributions": [
"translation"
]
},
{
"login": "JigSawFr",
"name": "JigSaw",
"avatar_url": "https://avatars.githubusercontent.com/u/5781907?v=4",
"profile": "https://github.com/JigSawFr",
"contributions": [
"code"
]
},
{
"login": "DireMunchkin",
"name": "DireMunchkin",
"avatar_url": "https://avatars.githubusercontent.com/u/1665676?v=4",
"profile": "https://github.com/DireMunchkin",
"contributions": [
"code"
]
},
{
"login": "FabioCingottini",
"name": "Fabio Cingottini",
"avatar_url": "https://avatars.githubusercontent.com/u/32102735?v=4",
"profile": "https://github.com/FabioCingottini",
"contributions": [
"translation"
]
},
{
"login": "itsrllyhim",
"name": "him",
"avatar_url": "https://avatars.githubusercontent.com/u/143047010?v=4",
"profile": "https://github.com/itsrllyhim",
"contributions": [
"code"
]
},
{
"login": "cchalop1",
"name": "CHALOPIN Clément",
"avatar_url": "https://avatars.githubusercontent.com/u/28163855?v=4",
"profile": "http://cchalop1.com",
"contributions": [
"code"
]
},
{
"login": "geetansh",
"name": "Geetansh Jindal",
"avatar_url": "https://avatars.githubusercontent.com/u/9976198?v=4",
"profile": "https://github.com/geetansh",
"contributions": [
"code"
]
},
{
"login": "0livier",
"name": "Olivier Garcia",
"avatar_url": "https://avatars.githubusercontent.com/u/10607?v=4",
"profile": "https://github.com/0livier",
"contributions": [
"code"
]
},
{
"login": "qcoudeyr",
"name": "qcoudeyr",
"avatar_url": "https://avatars.githubusercontent.com/u/124463277?v=4",
"profile": "https://github.com/qcoudeyr",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "runtipi",
"projectOwner": "runtipi",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true,
"commitConvention": "angular",
"commitType": "docs"
}

View file

@ -1,15 +0,0 @@
{
"image": "mcr.microsoft.com/vscode/devcontainers/javascript-node",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker": {
"version": "latest",
"moby": true
}
},
"extensions": [
"ms-azuretools.vscode-docker",
"ms-vscode.vscode-typescript-next",
"waderyan.gitblame"
],
"postCreateCommand": "./.devcontainer/postCreateCommand.sh"
}

View file

@ -1,5 +0,0 @@
#!/usr/bin/env bash
# We need to change the owner of the files in the app-data folder
# if this failes we have to change the permission your self
fswatch --event=Created /workspaces/runtipi/app-data/ | \
xargs -l1 sh -c 'echo "$1" && sudo chown node "$1" -R' -- &

View file

@ -1,12 +0,0 @@
#!/usr/bin/env bash
echo '{
"appsRepoUrl": "https://github.com/runtipi/runtipi-appstore.git/"
}' > state/settings.json
npm i -g pnpm
pnpm i
sudo apt-get update
sudo apt-get install jq fswatch -y
mkdir logs
mkdir data
sudo chown node logs
sudo chown node data

View file

@ -1,26 +0,0 @@
**/node_modules
**/.next
/node_modules
/.next
node_modules
.next
dist/
# all docker-compose files
docker-compose*.yml
Dockerfile*
.dockerignore
# Tipi folder
logs/
state/
templates/
scripts/
screenshots/
repos/
media/
data/
apps/
app-data/
.github/
__mocks__/

View file

@ -1,22 +0,0 @@
APPS_REPO_ID=7a92c8307e0a8074763c80be1fcfa4f87da6641daea9211aea6743b0116aba3b
APPS_REPO_URL=https://github.com/runtipi/runtipi-appstore
TZ=Etc/UTC
INTERNAL_IP=localhost
DNS_IP=9.9.9.9
ARCHITECTURE=arm64
TIPI_VERSION=1.5.2
JWT_SECRET=secret
ROOT_FOLDER_HOST=/path/to/runtipi
STORAGE_PATH=/path/to/runtipi
NGINX_PORT=7000
NGINX_PORT_SSL=443
DOMAIN=tipi.localhost
POSTGRES_HOST=tipi-db
POSTGRES_DBNAME=tipi
POSTGRES_USERNAME=tipi
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432
REDIS_HOST=tipi-redis
REDIS_PASSWORD=redis
DEMO_MODE=false
LOCAL_DOMAIN=tipi.lan

View file

@ -1,14 +0,0 @@
POSTGRES_HOST=localhost
POSTGRES_DBNAME=postgres
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5433
APPS_REPO_ID=repo-id
APPS_REPO_URL=https://test.com/test
REDIS_HOST=localhost
REDIS_PASSWORD=redis
INTERNAL_IP=localhost
TIPI_VERSION=1
JWT_SECRET=secret
DOMAIN=tipi.localhost
LOCAL_DOMAIN=tipi.lan

View file

@ -1,5 +0,0 @@
*.config.js
.eslintrc.js
next.config.js
jest.config.js
packages/

View file

@ -1,80 +0,0 @@
module.exports = {
plugins: ['@typescript-eslint', 'import', 'react', 'jest', 'jsx-a11y', 'testing-library', 'jest-dom'],
extends: [
'plugin:@typescript-eslint/recommended',
'next/core-web-vitals',
'next',
'airbnb',
'airbnb-typescript',
'eslint:recommended',
'plugin:import/typescript',
'prettier',
'plugin:react/recommended',
'plugin:jsx-a11y/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'no-restricted-exports': 0,
'no-redeclare': 0, // already handled by @typescript-eslint/no-redeclare
'react/display-name': 0,
'react/prop-types': 0,
'react/function-component-definition': 0,
'react/require-default-props': 0,
'import/prefer-default-export': 0,
'react/jsx-props-no-spreading': 0,
'react/no-unused-prop-types': 0,
'react/button-has-type': 0,
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'esbuild.js',
'e2e/**',
'**/*.test.{ts,tsx}',
'**/*.spec.{ts,tsx}',
'**/*.factory.{ts,tsx}',
'**/mocks/**',
'**/__mocks__/**',
'tests/**',
'**/*.d.ts',
'**/*.workspace.ts',
'**/*.setup.{ts,js}',
'**/*.config.{ts,js}',
],
},
],
'no-underscore-dangle': 0,
'arrow-body-style': 0,
'class-methods-use-this': 0,
'import/extensions': [
'error',
'ignorePackages',
{
'': 'never',
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
overrides: [
{
files: ['*.test.ts', '*.test.tsx'],
extends: ['plugin:jest-dom/recommended', 'plugin:testing-library/react'],
},
],
globals: {
JSX: true,
NodeJS: true,
},
env: {
'jest/globals': true,
},
};

13
.github/FUNDING.yml vendored
View file

@ -1,13 +0,0 @@
# These are supported funding model platforms
github: [meienberger] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: meienberger # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View file

@ -1,13 +1,17 @@
---
name: App Request
about: Suggest an app to be added
title: "[APP REQUEST]"
title: "[REQUEST]"
labels: app request
assignees: meienberger
---
## Read before submitting
Apps are now hosted in a secondary repository. Please open your app request issues [there](https://github.com/meienberger/runtipi-appstore)
**Describe app**
A clear and concise description of what the app consists of and how it would benefit the users of Tipi.
You can also easily contribute and propose a new app or update. Please check out our [Wiki](https://github.com/meienberger/runtipi/wiki/Adding-your-own-app) on how to do so
**Links**
Include links to all the available resources for the app. (eg: github repo, website, license)
**Additional context**
Add any other context or screenshots about the app request here.

View file

@ -7,12 +7,6 @@ assignees: meienberger
---
### Checklist
Before opening your issue be sure to have completed all those tasks.
- [ ] I have searched for an already existing issue with similar context and errors. My issue has not yet been reported.
- [ ] I have included a clear description and steps to reproduce.
- [ ] I have included logs from the file `runtipi/logs/error.log` if relevant
**Describe the bug**
A clear and concise description of what the bug is.
@ -29,10 +23,16 @@ A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- OS: [e.g. Ubuntu 20.04]
- Tipi Version [e.g. 2.0.5] (can be found in settings page)
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Please include logs here `runtipi/logs/error.log` and add any other context about the problem here. Like results of the `start` script or container logs `docker logs ...`
Add any other context about the problem here.

View file

@ -1,20 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
versioning-strategy: increase
schedule:
interval: 'daily'
open-pull-requests-limit: 10
rebase-strategy: 'auto'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'daily'
rebase-strategy: 'auto'

17
.github/stale.yml vendored
View file

@ -1,17 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions. If you feel this issue should remain open, please
add a comment with the requested information and we will keep it open.
closeComment: false

View file

@ -1,165 +0,0 @@
name: Alpha Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Alpha version tag (1, 2, 3, ...)'
required: true
jobs:
create-tag:
runs-on: ubuntu-latest
outputs:
tagname: ${{ steps.get_tag.outputs.tagname }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get tag from package.json
id: get_tag
run: |
VERSION=$(npm run version --silent)
echo "tagname=v${VERSION}-alpha.${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
- uses: rickstaa/action-create-tag@v1
with:
tag: ${{ steps.get_tag.outputs.tagname }}
build-worker:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
file: ./packages/worker/Dockerfile
platforms: linux/amd64
push: true
tags: ghcr.io/${{ github.repository_owner }}/worker:${{ needs.create-tag.outputs.tagname }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache,mode=max
build-images:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: ghcr.io/${{ github.repository_owner }}/runtipi:${{ needs.create-tag.outputs.tagname }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache,mode=max
build-cli:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v2.4.0
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set version
run: pnpm -r --filter cli set-version ${{ needs.create-tag.outputs.tagname }}
- name: Build CLI
run: pnpm -r --filter cli package
- name: Upload CLI
uses: actions/upload-artifact@v3
with:
name: cli
path: packages/cli/dist
publish-release:
runs-on: ubuntu-latest
needs: [create-tag, build-images, build-cli, build-worker]
steps:
- name: Download CLI
uses: actions/download-artifact@v3
with:
name: cli
path: cli
- name: Rename CLI
run: |
mv cli/bin/cli-x64 ./runtipi-cli-linux-x64
- name: Create alpha release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
**${{ needs.create-tag.outputs.tagname }}**
tag_name: ${{ needs.create-tag.outputs.tagname }}
name: ${{ needs.create-tag.outputs.tagname }}
draft: false
prerelease: true
files: |
runtipi-cli-linux-x64

View file

@ -1,175 +0,0 @@
name: Beta Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Beta version tag (1, 2, 3, ...)'
required: true
jobs:
create-tag:
runs-on: ubuntu-latest
outputs:
tagname: ${{ steps.get_tag.outputs.tagname }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get tag from package.json
id: get_tag
run: |
VERSION=$(npm run version --silent)
echo "tagname=v${VERSION}-beta.${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
- uses: rickstaa/action-create-tag@v1
with:
tag: ${{ steps.get_tag.outputs.tagname }}
build-worker:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
file: ./packages/worker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository_owner }}/worker:${{ needs.create-tag.outputs.tagname }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache,mode=max
build-images:
needs: create-tag
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository_owner }}/runtipi:${{ needs.create-tag.outputs.tagname }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache,mode=max
build-cli:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v2.4.0
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set version
run: pnpm -r --filter cli set-version ${{ needs.create-tag.outputs.tagname }}
- name: Build CLI
run: pnpm -r --filter cli package
- name: Upload CLI
uses: actions/upload-artifact@v3
with:
name: cli
path: packages/cli/dist
publish-release:
runs-on: ubuntu-latest
needs: [create-tag, build-images, build-cli, build-worker]
outputs:
id: ${{ steps.create_release.outputs.id }}
steps:
- name: Download CLI
uses: actions/download-artifact@v3
with:
name: cli
path: cli
- name: Rename CLI
run: |
mv cli/bin/cli-x64 ./runtipi-cli-linux-x64
mv cli/bin/cli-arm64 ./runtipi-cli-linux-arm64
- name: Create beta release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
**${{ needs.create-tag.outputs.tagname }}**
tag_name: ${{ needs.create-tag.outputs.tagname }}
name: ${{ needs.create-tag.outputs.tagname }}
draft: false
prerelease: true
files: |
runtipi-cli-linux-x64
runtipi-cli-linux-arm64
e2e-tests:
needs: [create-tag, publish-release]
uses: './.github/workflows/e2e.yml'
secrets: inherit
with:
version: ${{ needs.create-tag.outputs.tagname }}

View file

@ -1,61 +1,35 @@
name: Tipi CI
on:
pull_request:
push:
env:
ROOT_FOLDER: /runtipi
JWT_SECRET: 'secret'
ROOT_FOLDER_HOST: /runtipi
APPS_REPO_ID: repo-id
INTERNAL_IP: localhost
REDIS_HOST: redis
REDIS_PASSWORD: redis
APPS_REPO_URL: https://repo.github.com/
DOMAIN: localhost
LOCAL_DOMAIN: tipi.lan
TIPI_VERSION: 0.0.1
POSTGRES_HOST: localhost
POSTGRES_DBNAME: postgres
POSTGRES_USERNAME: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_PORT: 5433
ROOT_FOLDER: /test
JWT_SECRET: "secret"
ROOT_FOLDER_HOST: /tipi
jobs:
tests:
ci:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: postgres
ports:
- 5433:5432
# set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 16
- uses: pnpm/action-setup@v2.4.0
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 8
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
@ -68,74 +42,8 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Run linter
run: pnpm run lint
- name: Run linter on packages
run: pnpm -r run lint
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@v2
- name: Run tests
run: pnpm run test --max-workers ${{ steps.cpu-cores.outputs.count }}
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
flags: app
- name: Run packages tests
run: pnpm -r test
- name: Upload CLI coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/cli/coverage/lcov.info
flags: cli
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v2.4.0
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Build client
run: npm run build
- name: Run tsc
run: pnpm run tsc
- name: Run packages tsc
run: pnpm -r run tsc
run: pnpm -r lint
- name: Run tests
run: pnpm -r test

View file

@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v1

View file

@ -1,203 +0,0 @@
name: E2E Tests
on:
workflow_call:
inputs:
version:
required: true
type: string
description: 'Version to test (e.g. v1.6.0-beta.1)'
outputs:
page_url:
description: 'URL of the deployed report'
value: ${{ jobs.report-deployment.outputs.page_url }}
workflow_dispatch:
inputs:
version:
required: true
type: string
description: 'Version to test (e.g. v1.6.0-beta.1)'
jobs:
deploy:
timeout-minutes: 15
runs-on: ubuntu-latest
outputs:
droplet_id: ${{ steps.create-droplet.outputs.droplet_id }}
droplet_ip: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
postgres_password: ${{ steps.get-postgres-password.outputs.postgres_password }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_KEY }}
known_hosts: unnecessary
name: id_rsa
- name: Get sha of last commit
id: get-sha
run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Create new Droplet
id: create-droplet
run: |
droplet_id=$(doctl compute droplet create runtipi-${{ steps.get-sha.outputs.sha }} \
--image ubuntu-20-04-x64 \
--size s-1vcpu-1gb \
--format ID \
--no-header \
--ssh-keys ${{ secrets.SSH_KEY_FINGERPRINT }})
echo "droplet_id=$droplet_id" >> $GITHUB_OUTPUT
- name: Wait for Droplet to become active
run: |
while ! doctl compute droplet get ${{ steps.create-droplet.outputs.droplet_id }} --format Status --no-header | grep -q "active"; do sleep 5; done
- name: Get Droplet IP address
id: get-droplet-ip
run: |
droplet_ip=$(doctl compute droplet get ${{ steps.create-droplet.outputs.droplet_id }} --format PublicIPv4 --no-header)
echo "droplet_ip=$droplet_ip" >> $GITHUB_OUTPUT
- name: Wait for SSH to be ready on Droplet
run: |
while ! ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa root@${{ steps.get-droplet-ip.outputs.droplet_ip }} "echo 'SSH is ready'"; do sleep 5; done
- name: Create docker group on Droplet
uses: fifsky/ssh-action@master
with:
command: |
groupadd docker
usermod -aG docker root
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
user: root
key: ${{ secrets.SSH_KEY }}
- name: Wait 90 seconds for Docker to be ready on Droplet
run: sleep 90
- name: Deploy app to Droplet
uses: fifsky/ssh-action@master
with:
command: |
echo 'Downloading install script from GitHub'
curl -s https://raw.githubusercontent.com/runtipi/runtipi/${{ inputs.version }}/scripts/install.sh > install.sh
chmod +x install.sh
echo 'Running install script'
./install.sh --version ${{ inputs.version }}
echo 'App deployed'
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
user: root # TODO: use non-root user
key: ${{ secrets.SSH_KEY }}
- name: Get POSTGRES_PASSWORD from .env file
id: get-postgres-password
run: |
postgres_password=$(ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa root@${{ steps.get-droplet-ip.outputs.droplet_ip }} "cat ./runtipi/.env | grep POSTGRES_PASSWORD | cut -d '=' -f2")
echo "postgres_password=$postgres_password" >> $GITHUB_OUTPUT
e2e:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: [deploy]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2.4.0
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Create .env.e2e file with Droplet IP
run: |
echo "SERVER_IP=${{ needs.deploy.outputs.droplet_ip }}" > .env.e2e
echo "POSTGRES_PASSWORD=${{ needs.deploy.outputs.postgres_password }}" >> .env.e2e
- name: Install dependencies
run: pnpm install
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
id: run-e2e
run: npm run test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
report-deployment:
runs-on: ubuntu-latest
needs: [e2e]
outputs:
page_url: ${{ steps.deployment.outputs.page_url }}
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
if: always()
steps:
- name: Download report artifact
uses: actions/download-artifact@v3
with:
name: playwright-report
path: playwright-report/
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: playwright-report/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
teardown:
runs-on: ubuntu-latest
if: always()
needs: [e2e, deploy]
steps:
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Delete Droplet
run: doctl compute droplet delete ${{ needs.deploy.outputs.droplet_id }} --force

98
.github/workflows/release-candidate.yml vendored Normal file
View file

@ -0,0 +1,98 @@
name: Release candidate
on:
pull_request:
branches:
- master
jobs:
# Build images and publish RCs to DockerHub
build-images:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: technote-space/get-diff-action@v6
with:
FILES: |
VERSION
- name: Ensure env.MATCHED_FILES has VERSION in it
id: check-version
run: |
if [[ -z "${{ env.MATCHED_FILES }}" ]]; then
echo "::error::VERSION not modified"
exit 1
fi
if [[ ! "${{ env.MATCHED_FILES }}" =~ VERSION ]]; then
echo "::error::VERSION not modified"
exit 1
fi
- uses: vishnudxb/cancel-workflow@v1.2
if: failure()
with:
repo: meienberger/runtipi
workflow_id: ${{ github.run_id }}
access_token: ${{ github.token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get tag from VERSION file
id: meta
run: |
VERSION=$(cat VERSION)
TAG=${VERSION}
echo "::set-output name=tag::${TAG}"
- name: Build and push dashboard
uses: docker/build-push-action@v2
with:
context: ./packages/dashboard
platforms: linux/amd64,linux/arm64
push: true
tags: meienberger/tipi-dashboard:rc-${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/tipi-dashboard:latest
cache-to: type=inline
- name: Build and push api
uses: docker/build-push-action@v2
with:
context: ./packages/system-api
platforms: linux/amd64,linux/arm64
push: true
tags: meienberger/tipi-api:rc-${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/tipi-api:latest
cache-to: type=inline
# Test installation script
# test-install:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@master
# - name: Check if user id 1000 exists
# run: |
# if [[ ! $(id -u 1000) -eq 1000 ]]; then
# echo "Creating user 1000"
# sudo useradd -u 1000 test
# fi
# id: check-user-id
# - name: Run install script
# run: sudo ./scripts/start.sh --rc --ci

View file

@ -1,192 +1,78 @@
name: Publish release
on:
workflow_dispatch:
push:
branches:
- master
jobs:
create-tag:
release:
runs-on: ubuntu-latest
needs: [build-images, build-cli]
outputs:
tagname: ${{ steps.get_tag.outputs.tagname }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@master
- name: Get tag from package.json
id: get_tag
run: |
VERSION=$(npm run version --silent)
echo "tagname=v${VERSION}" >> $GITHUB_OUTPUT
- uses: rickstaa/action-create-tag@v1
- name: Create Tag
id: create_tag
uses: jaywcjlove/create-tag-action@v1.1.5
with:
tag: ${{ steps.get_tag.outputs.tagname }}
build-images:
if: github.repository == 'runtipi/runtipi'
needs: create-tag
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository_owner }}/runtipi:${{ needs.create-tag.outputs.tagname }},ghcr.io/${{ github.repository_owner }}/runtipi:latest
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/runtipi:buildcache,mode=max
build-worker:
runs-on: ubuntu-latest
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v5
with:
context: .
file: ./packages/worker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository_owner }}/worker:${{ needs.create-tag.outputs.tagname }},ghcr.io/${{ github.repository_owner }}/worker:latest
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/worker:buildcache,mode=max
build-cli:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: create-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v2.4.0
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Set version
run: pnpm -r --filter cli set-version ${{ needs.create-tag.outputs.tagname }}
- name: Build CLI
run: pnpm -r --filter cli package
- name: Upload CLI
uses: actions/upload-artifact@v3
with:
name: cli
path: packages/cli/dist
publish-release:
runs-on: ubuntu-latest
needs: [create-tag, build-images, build-worker, build-cli]
outputs:
id: ${{ steps.create_release.outputs.id }}
steps:
- name: Download CLI
uses: actions/download-artifact@v3
with:
name: cli
path: cli
- name: Rename CLI
run: |
mv cli/bin/cli-x64 ./runtipi-cli-linux-x64
mv cli/bin/cli-arm64 ./runtipi-cli-linux-arm64
- name: Create release
token: ${{ secrets.GITHUB_TOKEN }}
package-path: ./package.json
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
uses: actions/create-release@latest
if: steps.create_tag.outputs.successful
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
**${{ needs.create-tag.outputs.tagname }}**
tag_name: ${{ needs.create-tag.outputs.tagname }}
name: ${{ needs.create-tag.outputs.tagname }}
tag_name: ${{ steps.create_tag.outputs.version }}
release_name: ${{ steps.create_tag.outputs.version }}
draft: false
prerelease: true
files: |
runtipi-cli-linux-x64
runtipi-cli-linux-arm64
prerelease: false
e2e-tests:
needs: [create-tag, publish-release]
uses: './.github/workflows/e2e.yml'
secrets: inherit
with:
version: ${{ needs.create-tag.outputs.tagname }}
# Promote release if e2e tests succeed
promote:
needs: [publish-release, e2e-tests]
docker:
runs-on: ubuntu-latest
steps:
- name: Promote release
uses: actions/github-script@v7
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const id = '${{ needs.publish-release.outputs.id }}';
github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: id,
draft: false,
prerelease: false
});
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Get tag from VERSION file
id: meta
run: |
VERSION=$(cat VERSION)
TAG=${VERSION}
echo "::set-output name=tag::${TAG}"
-
name: Build and push dashboard
uses: docker/build-push-action@v2
with:
context: ./packages/dashboard
platforms: linux/amd64,linux/arm64
push: true
tags: meienberger/tipi-dashboard:latest,meienberger/tipi-dashboard:${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/tipi-dashboard:latest
cache-to: type=inline
-
name: Build and push api
uses: docker/build-push-action@v2
with:
context: ./packages/system-api
platforms: linux/amd64,linux/arm64
push: true
tags: meienberger/tipi-api:latest,meienberger/tipi-api:${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/tipi-api:latest
cache-to: type=inline

83
.gitignore vendored
View file

@ -1,69 +1,28 @@
*.swo
*.swp
.DS_Store
.vscode
.idea
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
/dist
server-preload.js
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
logs
.pnpm-debug.log
.env
.env*
!.env.example
!.env.test
github.secrets
node_modules/
/app-data/
/data/
/repos/
/apps/
/traefik/
nginx/*
letsencrypt/*
app-data/*
traefik/ssl/*
!traefik/ssl/.gitkeep
!app-data/.gitkeep
!letsencrypt/mkcert/.gitkeep
# media folder
media
state/*
!state/.gitkeep
/state/
/test-results/
/playwright-report/
/playwright/.cache/
temp
tipi.config.json
./traefik/
/user-config/
# Commit empty directories
!nignx/.gitkeep
media/data/movies/*
media/data/tv/*
!media/data/movies/.gitkeep
!media/data/tv/.gitkeep
media/torrents/*
!media/torrents/.gitkeep

5
.husky/pre-commit Executable file
View file

@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm -r test
pnpm -r lint

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"yaml.schemas": {
"https://raw.githubusercontent.com/ansible-community/schemas/main/f/ansible-playbook.json": "file:///Users/nicolas/Projects/runtipi/ansible/playbooks/install-dependencies.yml"
}
}

View file

@ -1,49 +0,0 @@
ARG NODE_VERSION="20.10"
ARG ALPINE_VERSION="3.18"
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS node_base
FROM node_base AS builder_base
RUN npm install pnpm -g
# BUILDER
FROM builder_base AS builder
WORKDIR /app
COPY ./pnpm-lock.yaml ./
COPY ./pnpm-workspace.yaml ./
COPY ./patches ./patches
RUN pnpm fetch --no-scripts
COPY ./package*.json ./
COPY ./packages/shared ./packages/shared
RUN pnpm install -r --prefer-offline
COPY ./src ./src
COPY ./tsconfig.json ./tsconfig.json
COPY ./next.config.mjs ./next.config.mjs
COPY ./public ./public
COPY ./tests ./tests
RUN npm run build
# APP
FROM node_base AS app
ENV NODE_ENV production
USER node
WORKDIR /app
COPY --from=builder /app/next.config.mjs ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
EXPOSE 3000
CMD ["npm", "run", "start"]

View file

@ -1,23 +0,0 @@
ARG NODE_VERSION="20.10"
ARG ALPINE_VERSION="3.18"
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION}
RUN npm install pnpm -g
WORKDIR /app
COPY ./pnpm-lock.yaml ./
COPY ./patches ./patches
RUN pnpm fetch --ignore-scripts
COPY ./package*.json ./
COPY ./packages/shared ./packages/shared
RUN pnpm install -r --prefer-offline
COPY ./tsconfig.json ./tsconfig.json
COPY ./next.config.mjs ./next.config.mjs
COPY ./public ./public
CMD ["npm", "run", "dev"]

167
README.md
View file

@ -1,137 +1,78 @@
# Tipi — A personal homeserver for everyone
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-42-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
[![License](https://img.shields.io/github/license/runtipi/runtipi)](https://github.com/runtipi/runtipi/blob/master/LICENSE)
[![Version](https://img.shields.io/github/v/release/runtipi/runtipi?color=%235351FB&label=version)](https://github.com/runtipi/runtipi/releases)
![Issues](https://img.shields.io/github/issues/runtipi/runtipi)
[![Docker Pulls](https://badgen.net/docker/pulls/meienberger/runtipi?icon=docker&label=pulls)](https://hub.docker.com/r/meienberger/runtipi/)
[![Docker Image Size](https://badgen.net/docker/size/meienberger/runtipi?icon=docker&label=image%20size)](https://hub.docker.com/r/meienberger/runtipi/)
![Build](https://github.com/runtipi/runtipi/workflows/Tipi%20CI/badge.svg)
[![Crowdin](https://badges.crowdin.net/runtipi/localized.svg)](https://crowdin.com/project/runtipi)
> 💡 Tipi is built with TypeScript, Next.js app router and Drizzle ORM! If you want to collaborate on a cool project, join the discussion on Discord!
#### Join the discussion
[![Discord](https://img.shields.io/discord/976934649643294750?label=discord&logo=discord)](https://discord.gg/Bu9qEPnHsc)
[![Matrix](https://img.shields.io/matrix/runtipi:matrix.org?label=matrix&logo=matrix)](https://matrix.to/#/#runtipi:matrix.org)
![Preview](https://raw.githubusercontent.com/runtipi/runtipi/develop/screenshots/appstore.png)
# ⛺️ Tipi — A personal homeserver for everyone
[![License](https://img.shields.io/github/license/meienberger/runtipi)](https://github.com/meienberger/runtipi/blob/master/LICENSE)
[![Version](https://img.shields.io/github/v/release/meienberger/runtipi?color=%235351FB&label=version)](https://github.com/meienberger/runtipi/releases)
![Issues](https://img.shields.io/github/issues/meienberger/runtipi)
[![Docker Pulls](https://badgen.net/docker/pulls/meienberger/tipi-dashboard?icon=docker&label=pulls)](https://hub.docker.com/r/meienberger/tipi-dashboard/)
[![Docker Image Size](https://badgen.net/docker/size/meienberger/tipi-dashboard?icon=docker&label=image%20size)](https://hub.docker.com/r/meienberger/tipi-dashboard/)
![RunsOn](https://img.shields.io/badge/Debian-Supported-green?logo=debian)
![RunsOn](https://img.shields.io/badge/Ubuntu-Supported-green?logo=ubuntu)
![Build](https://github.com/meienberger/runtipi/workflows/Tipi%20CI/badge.svg)
![Preview](https://raw.githubusercontent.com/meienberger/runtipi/develop/screenshots/1.png)
> ⚠️ Tipi is still at an early stage of development and issues are to be expected. Feel free to open an issue or pull request if you find a bug.
Tipi is a personal homeserver orchestrator that makes it easy to manage and run multiple services on a single server. It is based on Docker and comes with a simple web interface to manage your services. Tipi is designed to be easy to use, so you don't have to worry about manual configuration or networking. Simply install Tipi on your server and use the web interface to add and manage services. You can see a list of available services in the [App Store repo](https://github.com/runtipi/runtipi-appstore) and request new ones if you don't see what you need. To get started, follow the installation instructions below.
Tipi is a personal homeserver orchestrator. It is running docker containers under the hood and provides a simple web interface to manage them. Every service comes with an opinionated configuration in order to remove the need for manual configuration and network setup.
## Getting started
## Apps available
- [Filebrowser](https://github.com/filebrowser/filebrowser) - Web File Browser
- [Freshrss](https://github.com/FreshRSS/FreshRSS) - A free, self-hostable RSS aggregator
- [Invidious](https://github.com/iv-org/invidious) - An alternative front-end to YouTube
- [Jackett](https://github.com/Jackett/Jackett) - API Support for your favorite torrent trackers
- [Jellyfin](https://github.com/jellyfin/jellyfin) - A media server for your home collection
- [Joplin](https://github.com/laurent22/joplin) - Privacy focused note-taking app
- [n8n](https://github.com/n8n-io/n8n) - Workflow Automation Tool
- [Nextcloud](https://github.com/nextcloud/server) - A safe home for all your data
- [Pihole](https://github.com/pi-hole/pi-hole) - A black hole for Internet advertisements
- [Radarr](https://github.com/Radarr/Radarr) - Movie collection manager for Usenet and BitTorrent users.
- [Sonarr](https://github.com/Sonarr/Sonarr) - TV show manager for Usenet and BitTorrent
- [Syncthing](https://github.com/syncthing/syncthing) - Continuous File Synchronization
- [Tailscale](https://github.com/tailscale/tailscale) - The easiest, most secure way to use WireGuard and 2FA.
- [Transmission](https://github.com/transmission/transmission) - Fast, easy, and free BitTorrent client
- [Wireguard Easy](https://github.com/WeeJeWel/wg-easy) - WireGuard VPN + Web-based Admin UI
## 🛠 Installation
### Installation Requirements
- Ubuntu 18.04 LTS or higher (or Debian 10)
Visit our website [runtipi.io](https://www.runtipi.io/docs/getting-started/installation?utm_source=github&utm_medium=README&utm_campaign=getting-started) for installation instructions, documentation and guides.
### Step 1. Download Tipi
Run this in an empty directory where you want to install Tipi.
## Demo
```bash
git clone https://github.com/meienberger/runtipi.git
```
You can try out a demo of Tipi at [demo.runtipi.io](https://demo.runtipi.io) using the following credentials:
### Step 2. Run Tipi
cd into the downloaded directory and run the start script.
username: user@runtipi.io
password: password
```bash
cd runtipi && sudo ./scripts/start.sh
```
## 📚 Documentation
The script will prompt you the ip address of the dashboard once configured.
Tipi will run by default on port 80. To select another port you can run the start script with the `--port` argument
For a detailed guide on how to install Tipi. This amazing article by @kycfree [Running a Home Server with Tipi](https://kyc3.life/running-a-home-server-with-tipi/)
```bash
sudo ./scripts/start.sh --port 7000
```
You can find more documentation and tutorials / FAQ on [runtipi.io](https://www.runtipi.io/docs/introduction?utm_source=github&utm_medium=README&utm_campaign=main-repo-docs)
To stop Tipi, run the stop script.
```bash
sudo ./scripts/stop.sh
```
## ❤️ Contributing
Tipi is made to be very easy to plug in new apps. We welcome and appreciate new contributions.
If you want to add a new app or feature, you can follow the [Contribution guide](https://www.runtipi.io/docs/contributing/adding-a-new-app) for instructions on how to do so.
We are looking for contributions of all kinds. If you know design, development, or have ideas for new features, please get in touch.
If you want to support a new app or feature, you can:
- Fork the repository and create a new branch for your changes.
- Create a pull request.
## 📜 License
[![License](https://img.shields.io/github/license/runtipi/runtipi)](https://github.com/runtipi/runtipi/blob/master/LICENSE)
[![License](https://img.shields.io/github/license/meienberger/runtipi)](https://github.com/meienberger/runtipi/blob/master/LICENSE)
Tipi is licensed under the GNU General Public License v3.0. TL;DR — You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build & install instructions.
## 🗣 Community
- [Matrix](https://matrix.to/#/#runtipi:matrix.org)<br />
- [Twitter](https://twitter.com/runtipi)
- [Telegram](https://t.me/+72-y10MnLBw2ZGI0)
- [Discord](https://discord.gg/Bu9qEPnHsc)
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://meienberger.dev/"><img src="https://avatars.githubusercontent.com/u/47644445?v=4?s=100" width="100px;" alt="Nicolas Meienberger"/><br /><sub><b>Nicolas Meienberger</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=meienberger" title="Code">💻</a> <a href="#infra-meienberger" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/runtipi/runtipi/commits?author=meienberger" title="Tests">⚠️</a> <a href="https://github.com/runtipi/runtipi/commits?author=meienberger" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ArneNaessens"><img src="https://avatars.githubusercontent.com/u/16622722?v=4?s=100" width="100px;" alt="ArneNaessens"/><br /><sub><b>ArneNaessens</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=ArneNaessens" title="Code">💻</a> <a href="#ideas-ArneNaessens" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/runtipi/runtipi/commits?author=ArneNaessens" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DrMxrcy"><img src="https://avatars.githubusercontent.com/u/58747968?v=4?s=100" width="100px;" alt="DrMxrcy"/><br /><sub><b>DrMxrcy</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=DrMxrcy" title="Code">💻</a> <a href="#ideas-DrMxrcy" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/runtipi/runtipi/commits?author=DrMxrcy" title="Tests">⚠️</a> <a href="#content-DrMxrcy" title="Content">🖋</a> <a href="#promotion-DrMxrcy" title="Promotion">📣</a> <a href="#question-DrMxrcy" title="Answering Questions">💬</a> <a href="https://github.com/runtipi/runtipi/pulls?q=is%3Apr+reviewed-by%3ADrMxrcy" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://cobre.dev"><img src="https://avatars.githubusercontent.com/u/36574329?v=4?s=100" width="100px;" alt="Cooper"/><br /><sub><b>Cooper</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=CobreDev" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JTruj1ll0923"><img src="https://avatars.githubusercontent.com/u/6656643?v=4?s=100" width="100px;" alt="JTruj1ll0923"/><br /><sub><b>JTruj1ll0923</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=JTruj1ll0923" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Stetsed"><img src="https://avatars.githubusercontent.com/u/33891782?v=4?s=100" width="100px;" alt="Stetsed"/><br /><sub><b>Stetsed</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=Stetsed" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/blushell"><img src="https://avatars.githubusercontent.com/u/3621606?v=4?s=100" width="100px;" alt="Jones_Town"/><br /><sub><b>Jones_Town</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=blushell" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://rushichaudhari.github.io/"><img src="https://avatars.githubusercontent.com/u/6279035?v=4?s=100" width="100px;" alt="Rushi Chaudhari"/><br /><sub><b>Rushi Chaudhari</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=rushic24" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rblaine95"><img src="https://avatars.githubusercontent.com/u/4052340?v=4?s=100" width="100px;" alt="Robert Blaine"/><br /><sub><b>Robert Blaine</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=rblaine95" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://sethforprivacy.com"><img src="https://avatars.githubusercontent.com/u/40500387?v=4?s=100" width="100px;" alt="Seth For Privacy"/><br /><sub><b>Seth For Privacy</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=sethforprivacy" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hqwuzhaoyi"><img src="https://avatars.githubusercontent.com/u/44605072?v=4?s=100" width="100px;" alt="Prajna"/><br /><sub><b>Prajna</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=hqwuzhaoyi" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/justincmoy"><img src="https://avatars.githubusercontent.com/u/14875982?v=4?s=100" width="100px;" alt="Justin Moy"/><br /><sub><b>Justin Moy</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=justincmoy" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dextreem"><img src="https://avatars.githubusercontent.com/u/11060652?v=4?s=100" width="100px;" alt="dextreem"/><br /><sub><b>dextreem</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=dextreem" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/iBicha"><img src="https://avatars.githubusercontent.com/u/17722782?v=4?s=100" width="100px;" alt="Brahim Hadriche"/><br /><sub><b>Brahim Hadriche</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=iBicha" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://andrewbrereton.com"><img src="https://avatars.githubusercontent.com/u/682893?v=4?s=100" width="100px;" alt="Andrew Brereton"/><br /><sub><b>Andrew Brereton</b></sub></a><br /><a href="#content-andrewbrereton" title="Content">🖋</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://fsackur.github.io/"><img src="https://avatars.githubusercontent.com/u/3678789?v=4?s=100" width="100px;" alt="Freddie Sackur"/><br /><sub><b>Freddie Sackur</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=fsackur" title="Code">💻</a> <a href="https://github.com/runtipi/runtipi/commits?author=fsackur" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://innocentius.github.io"><img src="https://avatars.githubusercontent.com/u/5344432?v=4?s=100" width="100px;" alt="Innocentius"/><br /><sub><b>Innocentius</b></sub></a><br /><a href="#translation-innocentius" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TetrisIQ"><img src="https://avatars.githubusercontent.com/u/24246993?v=4?s=100" width="100px;" alt="Alex"/><br /><sub><b>Alex</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=TetrisIQ" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ryanc.cc"><img src="https://avatars.githubusercontent.com/u/21301288?v=4?s=100" width="100px;" alt="Ryan Wang"/><br /><sub><b>Ryan Wang</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=ruibaby" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/simonandr"><img src="https://avatars.githubusercontent.com/u/48092304?v=4?s=100" width="100px;" alt="simonandr"/><br /><sub><b>simonandr</b></sub></a><br /><a href="#content-simonandr" title="Content">🖋</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/demizeu"><img src="https://avatars.githubusercontent.com/u/121183951?v=4?s=100" width="100px;" alt="iepure"/><br /><sub><b>iepure</b></sub></a><br /><a href="#translation-demizeu" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SergeyKodolov"><img src="https://avatars.githubusercontent.com/u/35339452?v=4?s=100" width="100px;" alt="Sergey Kodolov"/><br /><sub><b>Sergey Kodolov</b></sub></a><br /><a href="#translation-SergeyKodolov" title="Translation">🌍</a> <a href="https://github.com/runtipi/runtipi/commits?author=SergeyKodolov" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sclaren"><img src="https://avatars.githubusercontent.com/u/915292?v=4?s=100" width="100px;" alt="sclaren"/><br /><sub><b>sclaren</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=sclaren" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mcmeel"><img src="https://avatars.githubusercontent.com/u/13773536?v=4?s=100" width="100px;" alt="mcmeel"/><br /><sub><b>mcmeel</b></sub></a><br /><a href="#question-mcmeel" title="Answering Questions">💬</a> <a href="#ideas-mcmeel" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/runtipi/runtipi/commits?author=mcmeel" title="Code">💻</a> <a href="https://github.com/runtipi/runtipi/commits?author=mcmeel" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NoisyFridge"><img src="https://avatars.githubusercontent.com/u/73795785?v=4?s=100" width="100px;" alt="NoisyFridge"/><br /><sub><b>NoisyFridge</b></sub></a><br /><a href="#translation-NoisyFridge" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Bvoxl"><img src="https://avatars.githubusercontent.com/u/67489519?v=4?s=100" width="100px;" alt="Bvoxl"/><br /><sub><b>Bvoxl</b></sub></a><br /><a href="#translation-Bvoxl" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/m-lab-0"><img src="https://avatars.githubusercontent.com/u/116570617?v=4?s=100" width="100px;" alt="m-lab-0"/><br /><sub><b>m-lab-0</b></sub></a><br /><a href="#translation-m-lab-0" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dannkunt"><img src="https://avatars.githubusercontent.com/u/32395839?v=4?s=100" width="100px;" alt="dannkunt"/><br /><sub><b>dannkunt</b></sub></a><br /><a href="#translation-dannkunt" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Schmanko"><img src="https://avatars.githubusercontent.com/u/94195393?v=4?s=100" width="100px;" alt="Schmanko"/><br /><sub><b>Schmanko</b></sub></a><br /><a href="#translation-Schmanko" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://micro.nghialele.com"><img src="https://avatars.githubusercontent.com/u/129353223?v=4?s=100" width="100px;" alt="Nghia Lele"/><br /><sub><b>Nghia Lele</b></sub></a><br /><a href="#translation-nghialele" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amusingimpala75"><img src="https://avatars.githubusercontent.com/u/69653100?v=4?s=100" width="100px;" alt="amusingimpala75"/><br /><sub><b>amusingimpala75</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=amusingimpala75" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://m1n.omg.lol"><img src="https://avatars.githubusercontent.com/u/54779580?v=4?s=100" width="100px;" alt="David"/><br /><sub><b>David</b></sub></a><br /><a href="#translation-M1n-4d316e" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/steveiliop56"><img src="https://avatars.githubusercontent.com/u/106091011?v=4?s=100" width="100px;" alt="Stavros Iliopoulos"/><br /><sub><b>Stavros Iliopoulos</b></sub></a><br /><a href="#translation-steveiliop56" title="Translation">🌍</a> <a href="https://github.com/runtipi/runtipi/commits?author=steveiliop56" title="Code">💻</a> <a href="https://github.com/runtipi/runtipi/commits?author=steveiliop56" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loxiry"><img src="https://avatars.githubusercontent.com/u/86959495?v=4?s=100" width="100px;" alt="loxiry"/><br /><sub><b>loxiry</b></sub></a><br /><a href="#translation-loxiry" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JigSawFr"><img src="https://avatars.githubusercontent.com/u/5781907?v=4?s=100" width="100px;" alt="JigSaw"/><br /><sub><b>JigSaw</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=JigSawFr" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DireMunchkin"><img src="https://avatars.githubusercontent.com/u/1665676?v=4?s=100" width="100px;" alt="DireMunchkin"/><br /><sub><b>DireMunchkin</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=DireMunchkin" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FabioCingottini"><img src="https://avatars.githubusercontent.com/u/32102735?v=4?s=100" width="100px;" alt="Fabio Cingottini"/><br /><sub><b>Fabio Cingottini</b></sub></a><br /><a href="#translation-FabioCingottini" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/itsrllyhim"><img src="https://avatars.githubusercontent.com/u/143047010?v=4?s=100" width="100px;" alt="him"/><br /><sub><b>him</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=itsrllyhim" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://cchalop1.com"><img src="https://avatars.githubusercontent.com/u/28163855?v=4?s=100" width="100px;" alt="CHALOPIN Clément"/><br /><sub><b>CHALOPIN Clément</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=cchalop1" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geetansh"><img src="https://avatars.githubusercontent.com/u/9976198?v=4?s=100" width="100px;" alt="Geetansh Jindal"/><br /><sub><b>Geetansh Jindal</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=geetansh" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/0livier"><img src="https://avatars.githubusercontent.com/u/10607?v=4?s=100" width="100px;" alt="Olivier Garcia"/><br /><sub><b>Olivier Garcia</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=0livier" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/qcoudeyr"><img src="https://avatars.githubusercontent.com/u/124463277?v=4?s=100" width="100px;" alt="qcoudeyr"/><br /><sub><b>qcoudeyr</b></sub></a><br /><a href="https://github.com/runtipi/runtipi/commits?author=qcoudeyr" title="Code">💻</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.1.4

View file

@ -1,35 +0,0 @@
import { fs, vol } from 'memfs';
const copyFolderRecursiveSync = (src: string, dest: string) => {
const exists = vol.existsSync(src);
const stats = vol.statSync(src);
const isDirectory = exists && stats.isDirectory();
if (isDirectory) {
vol.mkdirSync(dest, { recursive: true });
vol.readdirSync(src).forEach((childItemName) => {
copyFolderRecursiveSync(`${src}/${childItemName}`, `${dest}/${childItemName}`);
});
} else {
vol.copyFileSync(src, dest);
}
};
export default {
...fs,
copySync: (src: string, dest: string) => {
copyFolderRecursiveSync(src, dest);
},
__resetAllMocks: () => {
vol.reset();
},
__applyMockFiles: (newMockFiles: Record<string, string>) => {
// Create folder tree
vol.fromJSON(newMockFiles, 'utf8');
},
__createMockFiles: (newMockFiles: Record<string, string>) => {
vol.reset();
// Create folder tree
vol.fromJSON(newMockFiles, 'utf8');
},
__printVol: () => console.log(vol.toTree()),
};

View file

@ -1,29 +0,0 @@
const values = new Map();
const expirations = new Map();
export const createClient = jest.fn(() => {
return {
isOpen: true,
connect: jest.fn(),
set: (key: string, value: string, exp: number) => {
values.set(key, value);
expirations.set(key, exp);
},
get: (key: string) => values.get(key),
quit: jest.fn(),
del: (key: string) => values.delete(key),
ttl: (key: string) => expirations.get(key),
on: jest.fn(),
keys: (key: string) => {
const keyprefix = key.substring(0, key.length - 1);
const keys = [];
// eslint-disable-next-line no-restricted-syntax
for (const [k] of values) {
if (k.startsWith(keyprefix)) {
keys.push(k);
}
}
return keys;
},
};
});

2
ansible/ansible.cfg Normal file
View file

@ -0,0 +1,2 @@
[defaults]
INVENTORY = hosts

View file

@ -0,0 +1,4 @@
packages:
- jq
- coreutils
- docker

2
ansible/hosts Normal file
View file

@ -0,0 +1,2 @@
[localhost]
tipi ansible_connection=local

9
ansible/setup.yml Normal file
View file

@ -0,0 +1,9 @@
---
- hosts: tipi
become: yes
tasks:
- import_tasks: ./tasks/common/essential.yml
- import_tasks: ./tasks/common/docker.yml
# - name: Reboot machine
# reboot:

6
ansible/stop.yml Normal file
View file

@ -0,0 +1,6 @@
---
- hosts: tipi
become: yes
tasks:
- import_tasks: ./tasks/common/teardown.yml

View file

@ -0,0 +1,67 @@
- name: Install docker
package:
name:
- docker
- ca-certificates
- curl
- gnupg
- lsb-release
state: latest
- name: Check lsb_release -cs
shell: lsb_release -is
register: lsb_release
- name: Add docker gpg key (Ubuntu)
shell: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
when: lsb_release.stdout == 'Ubuntu'
- name: Add docker gpg key (Debian)
shell: curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
when: lsb_release.stdout == 'Debian'
- name: Add deb repo for docker (Ubuntu)
shell: echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
when: lsb_release.stdout == 'Ubuntu'
- name: Add deb repo for docker (Debian)
shell: echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
when: lsb_release.stdout == 'Debian'
- name: Update packages
apt:
update_cache: yes
upgrade: yes
- name: Install essential packages
package:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: latest
- name: Check if docker-compose is installed
stat:
path: /usr/local/bin/docker-compose
register: docker_compose_status
- name: Install docker-compose
shell: 'curl -L "https://github.com/docker/compose/releases/download/v2.3.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose'
when: not docker_compose_status.stat.exists
- name: Make docker-compose executable
shell: chmod +x /usr/local/bin/docker-compose
- name: Create group docker
group:
name: docker
- name: Put user in docker group
shell: usermod -aG docker {{ username }}
- name: Start docker service
service:
enabled: yes
name: docker
state: started

View file

@ -0,0 +1,33 @@
- name: Update packages
apt:
update_cache: yes
upgrade: yes
- name: Install essential packages
package:
name: "{{ packages }}"
state: latest
- name: Upgrade packages
apt:
upgrade: yes
- name: Add user to root group
user:
name: "{{ username }}"
group: root
- name: "Enable passwordless sudo for {{ username }}"
lineinfile:
dest: /etc/sudoers
regexp: "^%wheel"
line: "{{ username }} ALL=(ALL) NOPASSWD: ALL"
validate: "/usr/sbin/visudo -cf %s"
- name: Create cron every minute running system-info.sh
cron:
name: "system-info"
user: "{{ username }}"
minute: "*/1"
job: "{{ playbook_dir }}/../scripts/system-info.sh"
ignore_errors: yes

View file

@ -0,0 +1,18 @@
- name: Check if pm2 is installed
become_user: "{{ username }}"
stat:
path: /usr/local/bin/pm2
register: pm2_status
- name: Check if app is already running
become_user: "{{ username }}"
shell: pm2 list
register: pm2_result
when: pm2_status.stat.exists
- name: Stop app
become_user: "{{ username }}"
shell: pm2 stop "system-api"
when:
- pm2_status.stat.exists
- pm2_result.stdout.find("system-api") != -1

View file

@ -0,0 +1,44 @@
# Network
- name: Install avahi
package:
name: avahi
state: latest
when: ansible_os_family == "Arch"
- name: Install avahi
package:
name: avahi-daemon
state: latest
when: ansible_os_family == "Debian"
- name: Disable and stop sytemd-resolved
service:
name: systemd-resolved
state: stopped
enabled: no
- name: Replace line in /etc/nsswitch.conf
lineinfile:
path: /etc/nsswitch.conf
regexp: '^hosts:.*'
line: 'hosts: mymachines mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] files myhostname dns'
- name: Allow port 5353 in UFW
community.general.ufw:
rule: allow
port: 5353
proto: udp
- name: Copy avahi template to /etc/avahi/services/tipi.service
copy:
src: "{{ playbook_dir }}/templates/avahi/tipi.service"
dest: /etc/avahi/services/tipi.service
group: avahi
owner: avahi
- name: Start and enable avahi-daemon
service:
name: avahi-daemon
state: restarted
enabled: yes
###

View file

@ -0,0 +1,16 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_http._tcp</type>
<port>80</port>
</service>
</service-group>
<!-- <service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_http._tcp</type>
<port>443</port>
</service>
</service-group> -->

45
apps/anonaddy/config.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "Anonaddy",
"port": 8084,
"id": "anonaddy",
"description": "",
"short_desc": "Anonymous email forwarding",
"author": "",
"source": "https://github.com/anonaddy/anonaddy",
"image": "https://avatars.githubusercontent.com/u/51450862?s=200&v=4",
"requirements": {
"ports": [25]
},
"form_fields": {
"username": {
"type": "text",
"label": "Username",
"required": true,
"env_variable": "ANONADDY_USERNAME"
},
"key": {
"type": "text",
"label": "App key",
"hint": "Application key for encrypter service. Generate one with : echo \"base64:$(openssl rand -base64 32)\"",
"required": true,
"env_variable": "ANONADDY_KEY"
},
"domain": {
"type": "fqdn",
"label": "Your email domain (eg. example.com)",
"max": 50,
"min": 3,
"required": true,
"env_variable": "ANONADDY_DOMAIN"
},
"secret": {
"type": "text",
"label": "App secret",
"hint": "Long random string used when hashing data for the anonymous replies",
"max": 50,
"min": 3,
"required": true,
"env_variable": "ANONADDY_SECRET"
}
}
}

View file

@ -0,0 +1,83 @@
version: "3.5"
services:
db:
image: mariadb:10.5
container_name: db-anonaddy
command:
- "mysqld"
- "--character-set-server=utf8mb4"
- "--collation-server=utf8mb4_unicode_ci"
volumes:
- "${APP_DATA_DIR}/db:/var/lib/mysql"
environment:
MARIADB_DATABASE: anonaddy
MARIADB_USER: anonaddy
MARIADB_ROOT_PASSWORD: anonaddy
MARIADB_PASSWORD: anonaddy
restart: unless-stopped
networks:
- tipi_main_network
redis:
image: redis:4.0-alpine
container_name: redis-anonaddy
restart: unless-stopped
networks:
- tipi_main_network
anonaddy:
image: anonaddy/anonaddy:0.11.1
container_name: anonaddy
ports:
- 25:25
- ${APP_PORT}:8000
depends_on:
- db
- redis
volumes:
- "${APP_DATA_DIR}/data:/data"
environment:
TZ: ${TZ}
DB_HOST: db-anonaddy
DB_PASSWORD: anonaddy
REDIS_HOST: redis-anonaddy
APP_KEY: ${ANONADDY_KEY}
ANONADDY_DOMAIN: ${ANONADDY_DOMAIN}
ANONADDY_SECRET: ${ANONADDY_SECRET}
ANONADDY_ADMIN_USERNAME: ${ANONADDY_USERNAME}
POSTFIX_DEBUG: true
restart: unless-stopped
networks:
- tipi_main_network
# labels:
# traefik.enable: true
# traefik.http.routers.anonaddy.rule: Host(`anonaddy.tipi.home`)
# traefik.http.routers.anonaddy.tls: true
# traefik.http.routers.anonaddy.entrypoints: websecure
# traefik.http.routers.anonaddy.service: anonaddy
# traefik.http.services.anonaddy.loadbalancer.server.port: 8000
# labels:
# traefik.enable: true
# traefik.http.routers.anonaddy.rule: PathPrefix(`/anonaddy`)
# # Redirect to ending /
# traefik.http.middlewares.anonaddy-redirect.redirectregex.regex: ^(http:\/\/(\[[\w:.]+\]|[\w\._-]+)(:\d+)?)\/anonaddy$$
# traefik.http.middlewares.anonaddy-redirect.redirectregex.replacement: $${1}/anonaddy/
# # Strip /anonaddy/ from URL
# traefik.http.middlewares.anonaddy-stripprefix.stripprefixregex.regex: (/anonaddy/|/anonaddy)
# traefik.http.middlewares.anonaddy-headers.headers.customrequestheaders.Host: $$host
# traefik.http.middlewares.anonaddy-headers.headers.customrequestheaders.X-Forwarded-Proto: $$scheme
# traefik.http.middlewares.anonaddy-headers.headers.customrequestheaders.X-Forwarded-For: $$proxy_add_x_forwarded_for
# traefik.http.middlewares.anonaddy-headers.headers.customrequestheaders.X-Real-IP: $$remote_addr
# traefik.http.middlewares.anonaddy-headers.headers.customrequestheaders.X-Frame-Options: "SAMEORIGIN"
# # Apply middleware
# traefik.http.routers.anonaddy.middlewares: anonaddy-redirect, anonaddy-stripprefix, anonaddy-headers
# traefik.http.routers.anonaddy.entrypoints: http
# traefik.http.routers.anonaddy.service: anonaddy
# traefik.http.services.anonaddy.loadbalancer.server.port: 8000

View file

@ -0,0 +1,5 @@
version: "3.7"
networks:
tipi_main_network:
name: runtipi_tipi_main_network

View file

@ -0,0 +1,13 @@
{
"name": "File Browser",
"available": true,
"port": 8096,
"id": "filebrowser",
"description": "Reliable and Performant File Management Desktop Sync and File Sharing\n Default credentials: admin / admin",
"short_desc": "Access your homeserver files from your browser",
"author": "",
"website": "https://filebrowser.org/",
"source": "https://github.com/filebrowser/filebrowser",
"image": "https://avatars.githubusercontent.com/u/35781395?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,8 @@
{
"port": 80,
"baseURL": "",
"address": "",
"log": "stdout",
"database": "/database/filebrowser.db",
"root": "/srv"
}

View file

@ -0,0 +1,16 @@
services:
filebrowser:
container_name: filebrowser
image: filebrowser/filebrowser:s6
ports:
- ${APP_PORT}:80
environment:
- PUID=1000
- PGID=1000
volumes:
- ${ROOT_FOLDER_HOST}/app-data:/srv/app-data
- ${ROOT_FOLDER_HOST}/media:/srv/media
- ${APP_DATA_DIR}/data/db:/database
- ${APP_DATA_DIR}/data/config:/config
networks:
- tipi_main_network

12
apps/filerun/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "FileRun",
"port": 8087,
"available": false,
"id": "filerun",
"description": "Reliable and Performant File Management Desktop Sync and File Sharing",
"short_desc": "Access your homeserver files from your browser",
"author": "FileRun, LDA - Portugal",
"source": "https://www.filerun.com/",
"image": "https://avatars.githubusercontent.com/u/6422152?v=4",
"form_fields": {}
}

View file

@ -0,0 +1,38 @@
services:
filerun-db:
container_name: filerun-db
user: 1000:1000
image: mariadb:10.1
environment:
MYSQL_ROOT_PASSWORD: tipi
MYSQL_USER: tipi
MYSQL_PASSWORD: tipi
MYSQL_DATABASE: tipi
volumes:
- ${APP_DATA_DIR}/data/db:/var/lib/mysql
networks:
- tipi_main_network
filerun:
container_name: filerun
image: filerun/filerun:arm64v8
environment:
FR_DB_HOST: filerun-db
FR_DB_PORT: 3306
FR_DB_NAME: tipi
FR_DB_USER: tipi
FR_DB_PASS: tipi
APACHE_RUN_USER: 1000
APACHE_RUN_GROUP: 1000
APACHE_RUN_USER_ID: 33
APACHE_RUN_GROUP_ID: 33
depends_on:
- db
links:
- db:db
ports:
- ${APP_PORT}:80
volumes:
- ${ROOT_FOLDER_HOST}/app-data/medias:/user-files
networks:
- tipi_main_network

12
apps/freshrss/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "FreshRSS",
"available": true,
"port": 8086,
"id": "freshrss",
"description": "FreshRSS is a self-hosted RSS feed aggregator like Leed or Kriss Feed.\nIt is lightweight, easy to work with, powerful, and customizable.\n\nIt is a multi-user application with an anonymous reading mode. It supports custom tags. There is an API for (mobile) clients, and a Command-Line Interface.\n\nThanks to the WebSub standard (formerly PubSubHubbub), FreshRSS is able to receive instant push notifications from compatible sources, such as Mastodon, Friendica, WordPress, Blogger, FeedBurner, etc.\n\nFreshRSS natively supports basic Web scraping, based on XPath, for Web sites not providing any RSS / Atom feed.\n\nFinally, it supports extensions for further tuning.",
"short_desc": "A free, self-hostable aggregator… ",
"author": "https://freshrss.org/",
"source": "https://github.com/FreshRSS/FreshRSS",
"image": "https://avatars.githubusercontent.com/u/9414285?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,25 @@
version: "3.7"
services:
freshrss:
image: lscr.io/linuxserver/freshrss:1.19.2
container_name: freshrss
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
volumes:
- ${APP_DATA_DIR}/data/freshrss:/config
ports:
- ${APP_PORT}:80
restart: unless-stopped
networks:
- tipi_main_network
# labels:
# traefik.enable: true
# traefik.http.routers.freshrss.rule: Host(`freshrss.tipi.home`)
# traefik.http.routers.freshrss.service: freshrss
# traefik.http.routers.freshrss.tls: true
# traefik.http.routers.freshrss.entrypoints: websecure
# traefik.http.services.freshrss.loadbalancer.server.port: 80

View file

@ -0,0 +1,12 @@
{
"name": "Invidious",
"available": true,
"port": 8095,
"id": "invidious",
"description": "",
"short_desc": "",
"author": "",
"source": "https://github.com/iv-org/invidious",
"image": "https://raw.githubusercontent.com/iv-org/invidious/master/assets/invidious-colored-vector.svg",
"form_fields": {}
}

View file

@ -0,0 +1,41 @@
version: "3"
services:
invidious:
user: 1000:1000
container_name: invidious
image: quay.io/invidious/invidious:latest-arm64
restart: unless-stopped
ports:
- "${APP_PORT}:3000"
environment:
INVIDIOUS_CONFIG: |
db:
dbname: invidious
user: tipi
password: tipi
host: invidious-db
port: 5432
check_tables: true
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
interval: 30s
timeout: 5s
retries: 2
depends_on:
- invidious-db
invidious-db:
user: 1000:1000
container_name: invidious-db
image: docker.io/library/postgres:14
restart: unless-stopped
volumes:
- ${APP_DATA_DIR}/data/postgres:/var/lib/postgresql/data
- ${APP_DATA_DIR}/data/sql:/config/sql
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
environment:
POSTGRES_DB: invidious
POSTGRES_USER: tipi
POSTGRES_PASSWORD: tipi
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]

View file

@ -0,0 +1,44 @@
version: "3"
services:
invidious:
user: 1000:1000
container_name: invidious
image: quay.io/invidious/invidious:latest
restart: unless-stopped
ports:
- "${APP_PORT}:3000"
environment:
# Please read the following file for a comprehensive list of all available
# configuration options and their associated syntax:
# https://github.com/iv-org/invidious/blob/master/config/config.example.yml
INVIDIOUS_CONFIG: |
db:
dbname: invidious
user: tipi
password: tipi
host: invidious-db
port: 5432
check_tables: true
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
interval: 30s
timeout: 5s
retries: 2
depends_on:
- invidious-db
invidious-db:
user: 1000:1000
container_name: invidious-db
image: docker.io/library/postgres:14
restart: unless-stopped
volumes:
- ${APP_DATA_DIR}/data/postgres:/var/lib/postgresql/data
- ${APP_DATA_DIR}/data/sql:/config/sql
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
environment:
POSTGRES_DB: invidious
POSTGRES_USER: tipi
POSTGRES_PASSWORD: tipi
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]

12
apps/jackett/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "Jackett",
"available": true,
"port": 8097,
"id": "jackett",
"description": "Jackett works as a proxy server: it translates queries from apps (Sonarr, Radarr, SickRage, CouchPotato, Mylar3, Lidarr, DuckieTV, qBittorrent, Nefarious etc.) into tracker-site-specific http queries, parses the html or json response, and then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches.",
"short_desc": "API Support for your favorite torrent trackers ",
"author": "",
"source": "https://github.com/Jackett/Jackett",
"image": "https://avatars.githubusercontent.com/u/15383019?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,20 @@
version: "3.7"
services:
jackett:
image: lscr.io/linuxserver/jackett
container_name: jackett
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
- AUTO_UPDATE=true
dns:
- ${DNS_IP}
volumes:
- ${APP_DATA_DIR}/data:/config
- ${ROOT_FOLDER_HOST}/media/torrents:/downloads
ports:
- ${APP_PORT}:9117
restart: unless-stopped
networks:
- tipi_main_network

12
apps/jellyfin/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "Jellyfin",
"available": true,
"port": 8091,
"id": "jellyfin",
"description": "",
"short_desc": "",
"author": "",
"source": "",
"image": "https://avatars.githubusercontent.com/u/45698031?s=200&v=4",
"form_fields": {}
}

View file

View file

@ -0,0 +1,18 @@
version: "3.7"
services:
jellyfin:
image: lscr.io/linuxserver/jellyfin
container_name: jellyfin
volumes:
- ${APP_DATA_DIR}/data/config:/config
- ${ROOT_FOLDER_HOST}/media/data:/data/media
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
restart: "unless-stopped"
ports:
- ${APP_PORT}:8096
networks:
- tipi_main_network

13
apps/joplin/README.md Normal file
View file

@ -0,0 +1,13 @@
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in Markdown format.
Notes exported from Evernote can be imported into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
The notes can be securely synchronised using end-to-end encryption with various cloud services including Nextcloud, Dropbox, OneDrive and Joplin Cloud.
Full text search is available on all platforms to quickly find the information you need. The app can be customised using plugins and themes, and you can also easily create your own.
The application is available for Windows, Linux, macOS, Android and iOS. A Web Clipper, to save web pages and screenshots from your browser, is also available for Firefox and Chrome.
## Credentials
Username: admin@localhost
Password: admin

13
apps/joplin/config.json Normal file
View file

@ -0,0 +1,13 @@
{
"name": "Joplin Server",
"available": true,
"port": 8099,
"id": "joplin",
"description": "Default credentials: admin@localhost / admin",
"short_desc": "Note taking and to-do application with synchronisation",
"author": "https://github.com/laurent22",
"source": "https://github.com/laurent22/joplin",
"website": "https://joplinapp.org",
"image": "https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/LinuxIcons/256x256.png",
"form_fields": {}
}

View file

@ -0,0 +1,38 @@
version: "3.7"
services:
db-joplin:
container_name: db-joplin
image: postgres:14.2
volumes:
- ${APP_DATA_DIR}/data/postgres:/var/lib/postgresql/data
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=tipi
- POSTGRES_USER=tipi
- POSTGRES_DB=joplin
networks:
- tipi_main_network
joplin:
container_name: joplin
image: florider89/joplin-server:2.7.4
restart: unless-stopped
depends_on:
- db-joplin
ports:
- ${APP_PORT}:22300
dns:
- ${DNS_IP}
environment:
- APP_PORT=22300
- APP_BASE_URL=http://${INTERNAL_IP}:${APP_PORT}
- DB_CLIENT=pg
- POSTGRES_PASSWORD=tipi
- POSTGRES_USER=tipi
- POSTGRES_DATABASE=joplin
- POSTGRES_PORT=5432
- POSTGRES_HOST=db-joplin
- MAX_TIME_DRIFT=0
networks:
- tipi_main_network

13
apps/n8n/config.json Normal file
View file

@ -0,0 +1,13 @@
{
"name": "n8n",
"available": true,
"port": 8094,
"id": "n8n",
"description": "n8n is an extendable workflow automation tool. With a fair-code distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything.",
"short_desc": "Workflow Automation Tool. Alternative to Zapier",
"author": "n8n.io",
"source": "https://github.com/n8n-io/n8n",
"website": "https://n8n.io/",
"image": "https://avatars.githubusercontent.com/u/45487711?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,36 @@
version: "3.7"
services:
db-n8n:
container_name: db-n8n
image: postgres:14.2
restart: on-failure
volumes:
- ${APP_DATA_DIR}/data/db:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=tipi
- POSTGRES_USER=tipi
- POSTGRES_DB=n8n
networks:
- tipi_main_network
n8n:
container_name: n8n
image: n8nio/n8n:0.174.0
restart: unless-stopped
ports:
- ${APP_PORT}:5678
volumes:
- ${APP_DATA_DIR}/data/n8n:/home/node/.n8n
command: /bin/sh -c "sleep 5; n8n start"
environment:
- DB-TYPE=postgresdb
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_HOST=db-n8n
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_USER=tipi
- DB_POSTGRESDB_PASSWORD=tipi
depends_on:
- db-n8n
networks:
- tipi_main_network

View file

@ -0,0 +1,29 @@
{
"name": "Nextcloud",
"available": true,
"port": 8083,
"id": "nextcloud",
"description": "Nextcloud is a self-hosted, open source, and fully-featured cloud storage solution for your personal files, office documents, and photos.",
"short_desc": "Productivity platform that keeps you in control",
"author": "Nextcloud GmbH",
"source": "https://github.com/nextcloud/server",
"image": "https://avatars.githubusercontent.com/u/19211038?s=200&v=4",
"form_fields": {
"username": {
"type": "text",
"label": "Username",
"max": 50,
"min": 3,
"required": true,
"env_variable": "NEXTCLOUD_ADMIN_USER"
},
"password": {
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "NEXTCLOUD_ADMIN_PASSWORD"
}
}
}

View file

View file

View file

@ -0,0 +1,67 @@
version: "3.7"
services:
db-nextcloud:
container_name: db-nextcloud
image: postgres:14.2
restart: on-failure
volumes:
- ${APP_DATA_DIR}/data/db:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=tipi
- POSTGRES_USER=tipi
- POSTGRES_DB=nextcloud
networks:
- tipi_main_network
redis-nextcloud:
container_name: redis-nextcloud
user: "1000:1000"
image: redis:6.2.6
restart: on-failure
volumes:
- "${APP_DATA_DIR}/data/redis:/data"
networks:
- tipi_main_network
cron:
image: nextcloud:23.0.3-apache
restart: on-failure
volumes:
- ${APP_DATA_DIR}/data/nextcloud:/var/www/html
entrypoint: /cron.sh
depends_on:
- db-nextcloud
- redis-nextcloud
networks:
- tipi_main_network
nextcloud:
container_name: nextcloud
image: nextcloud:23.0.3-apache
restart: unless-stopped
ports:
- ${APP_PORT}:80
volumes:
- ${APP_DATA_DIR}/data/nextcloud:/var/www/html
environment:
- POSTGRES_HOST=db-nextcloud
- REDIS_HOST=redis-nextcloud
- POSTGRES_PASSWORD=tipi
- POSTGRES_USER=tipi
- POSTGRES_DB=nextcloud
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- NEXTCLOUD_TRUSTED_DOMAINS=${INTERNAL_IP}:${APP_PORT}
depends_on:
- db-nextcloud
- redis-nextcloud
networks:
- tipi_main_network
# labels:
# traefik.enable: true
# traefik.http.routers.nextcloud.rule: Host(`nextcloud.tipi.home`)
# traefik.http.routers.nextcloud.service: nextcloud
# traefik.http.routers.nextcloud.tls: true
# traefik.http.routers.nextcloud.entrypoints: websecure
# traefik.http.services.nextcloud.loadbalancer.server.port: 80

24
apps/pihole/config.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "PiHole",
"available": true,
"port": 8081,
"requirements": {
"ports": [53]
},
"id": "pihole",
"description": "",
"short_desc": "",
"author": "",
"source": "",
"image": "https://avatars.githubusercontent.com/u/16827203?s=200&v=4",
"form_fields": {
"password": {
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "APP_PASSWORD"
}
}
}

View file

@ -0,0 +1,6 @@
# A Record
#local-data: "somecomputer.local. A 192.168.1.1"
# PTR Record
#local-data-ptr: "192.168.1.1 somecomputer.local."

View file

@ -0,0 +1,92 @@
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . <file>"
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: December 07, 2021
; related version of root zone: 2021120701
;
; FORMERLY NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 199.9.14.201
B.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:200::b
;
; FORMERLY C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
. 3600000 NS D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
. 3600000 NS E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
. 3600000 NS F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
. 3600000 NS G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
. 3600000 NS H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
. 3600000 NS I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
. 3600000 NS K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
;
; OPERATED BY ICANN
;
. 3600000 NS L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
;
; OPERATED BY WIDE
;
. 3600000 NS M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
; End of file

View file

@ -0,0 +1,9 @@
; autotrust trust anchor file
;;id: . 1
;;last_queried: 1650921300 ;;Mon Apr 25 21:15:00 2022
;;last_success: 1650921300 ;;Mon Apr 25 21:15:00 2022
;;next_probe_time: 1650962281 ;;Tue Apr 26 08:38:01 2022
;;query_failed: 0
;;query_interval: 43200
;;retry_time: 8640
. 86400 IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1650921210 ;;Mon Apr 25 21:13:30 2022

View file

@ -0,0 +1,136 @@
# https://linux.die.net/man/5/unbound.conf
# https://docs.pi-hole.net/guides/unbound/
server:
# Enable or disable whether the unbound server forks into the background
# as a daemon. Default is yes.
do-daemonize: no
# If given, after binding the port the user privileges are dropped.
# Default is "unbound". If you give username: "" no user change is performed.
username: ""
# No need to chroot as this container has been stripped of all other binaries.
chroot: ""
# If "" is given, logging goes to stderr, or nowhere once daemonized.
logfile: ""
# The process id is written to the file. Not required since we are running
# in a container with one process.
pidfile: ""
# The verbosity number, level 0 means no verbosity, only errors.
# Level 1 gives operational information.
# Level 2 gives detailed operational information.
# Level 3 gives query level information, output per query.
# Level 4 gives algorithm level information.
# Level 5 logs client identification for cache misses.
# Default is level 1. The verbosity can also be increased from the commandline.
verbosity: 1
# Listen on all ipv4 interfaces, answer queries from the local subnet.
interface: 0.0.0.0
# The port number, default 53, on which the server responds to queries.
port: 53
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
# Reduce EDNS reassembly buffer size (see also https://docs.pi-hole.net/guides/dns/unbound/ )
# IP fragmentation is unreliable on the Internet today, and can cause
# transmission failures when large DNS messages are sent via UDP. Even
# when fragmentation does work, it may not be secure; it is theoretically
# possible to spoof parts of a fragmented DNS message, without easy
# detection at the receiving end. Recently, there was an excellent study
# >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
# in collaboration with NLnet Labs explored DNS using real world data from the
# the RIPE Atlas probes and the researchers suggested different values for
# IPv4 and IPv6 and in different scenarios. They advise that servers should
# be configured to limit DNS messages sent over UDP to a size that will not
# trigger fragmentation on typical network links. DNS servers can switch
# from UDP to TCP when a DNS response is too big to fit in this limited
# buffer size. This value has also been suggested in DNS Flag Day 2020.
edns-buffer-size: 1232
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines.
# In reality for most users running on small networks or on a single machine,
# it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 1
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
# (requires CAP_NET_ADMIN or privileged)
# so-rcvbuf: 1m
# The netblock is given as an IP4 or IP6 address with /size appended for a
# classless network block. The action can be deny, refuse, allow or allow_snoop.
access-control: 127.0.0.1/32 allow
access-control: 192.168.0.0/16 allow
access-control: 172.16.0.0/12 allow
access-control: 10.0.0.0/8 allow
access-control: 100.64.0.0/10 allow
access-control: 10.21.21.0/24 allow
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
# Read the root hints from this file. Default is nothing, using built in
# hints for the IN class. The file has the format of zone files, with root
# nameserver names and addresses only. The default may become outdated,
# when servers change, therefore it is good practice to use a root-hints
# file. get one from https://www.internic.net/domain/named.root
root-hints: /etc/unbound/root.hints
# File with trust anchor for one zone, which is tracked with RFC5011 probes.
# The probes are several times per month, thus the machine must be online frequently.
# The initial file can be one with contents as described in trust-anchor-file.
# The file is written to when the anchor is updated, so the unbound user must
# have write permission.
auto-trust-anchor-file: /etc/unbound/root.key
# Number of ports to open. This number of file descriptors can be opened per thread.
# Must be at least 1. Default depends on compile options. Larger numbers need extra
# resources from the operating system. For performance a very large value is best,
# use libevent to make this possible.
outgoing-range: 8192
# The number of queries that every thread will service simultaneously. If more queries
# arrive that need servicing, and no queries can be jostled out (see jostle-timeout),
# then the queries are dropped. This forces the client to resend after a timeout;
# allowing the server time to work on the existing queries. Default depends on
# compile options, 512 or 1024.
num-queries-per-thread: 4096
include: /etc/unbound/a-records.conf
# forward-zone:
# name: "."
# forward-addr: 194.242.2.3@853 # Mullvad primary
# forward-addr: 193.19.108.3@853 # Mullvad secondary

View file

@ -0,0 +1,39 @@
version: "3.7"
services:
unbound:
image: "klutchell/unbound"
container_name: unbound
restart: unless-stopped
volumes:
- "${APP_DATA_DIR}/data/unbound:/etc/unbound"
networks:
tipi_main_network:
ipv4_address: 10.21.21.200
pihole:
depends_on: [unbound]
container_name: pihole
image: pihole/pihole:latest
restart: unless-stopped
hostname: pihole
dns:
- 127.0.0.1
# - 10.21.21.200 # Points to unbound
ports:
- 53:53/tcp
- 53:53/udp
- ${APP_PORT}:80
volumes:
- ${APP_DATA_DIR}/data/pihole:/etc/pihole
- ${APP_DATA_DIR}/data/dnsmasq:/etc/dnsmasq.d
environment:
TZ: ${TZ}
WEBPASSWORD: ${APP_PASSWORD}
# PIHOLE_DNS_: 10.21.21.200 # Points to unbound
FTLCONF_REPLY_ADDR4: 10.21.21.201
cap_add:
- NET_ADMIN
networks:
tipi_main_network:
ipv4_address: 10.21.21.201

12
apps/radarr/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "Radarr",
"available": true,
"port": 8088,
"id": "radarr",
"description": "",
"short_desc": "",
"author": "",
"source": "",
"image": "https://avatars.githubusercontent.com/u/25025331?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,20 @@
version: "3.7"
services:
radarr:
image: lscr.io/linuxserver/radarr
container_name: radarr
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
dns:
- ${DNS_IP}
volumes:
- ${APP_DATA_DIR}/data:/config
- ${ROOT_FOLDER_HOST}/media/data/movies:/movies #optional
- ${ROOT_FOLDER_HOST}/media/torrents:/downloads #optional
ports:
- ${APP_PORT}:7878
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,11 @@
{
"name": "Simple Torrent",
"port": 8085,
"id": "simple-torrent",
"description": "SimpleTorrent is a a self-hosted remote torrent client, written in Go (golang). Started torrents remotely, download sets of files on the local disk of the server, which are then retrievable or streamable via HTTP.",
"short_desc": "A self-hosted remote torrent client",
"author": "",
"source": "https://github.com/boypt/simple-torrent",
"image": "https://getumbrel.github.io/umbrel-apps-gallery/simple-torrent/icon.svg",
"form_fields": {}
}

View file

@ -0,0 +1,25 @@
version: "3.7"
services:
server:
container_name: simple-torrent
image: boypt/cloud-torrent:1.3.9
restart: on-failure
ports:
- ${APP_PORT}:${APP_PORT}
command: >
--port=${APP_PORT}
--config-path /config/simple-torrent.json
volumes:
- ${APP_DATA_DIR}/data/torrents:/torrents
- ${APP_DATA_DIR}/data/downloads:/downloads
- ${APP_DATA_DIR}/data/config:/config
networks:
- tipi_main_network
# labels:
# traefik.enable: true
# traefik.http.routers.simple-torrent.rule: Host(`simple-torrent.tipi.home`)
# traefik.http.routers.simple-torrent.service: simple-torrent
# traefik.http.routers.simple-torrent.tls: true
# traefik.http.routers.simple-torrent.entrypoints: websecure
# traefik.http.services.simple-torrent.loadbalancer.server.port: ${APP_SIMPLETORRENT_PORT}

12
apps/sonarr/config.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "Sonarr",
"available": true,
"port": 8098,
"id": "sonarr",
"description": "",
"short_desc": "",
"author": "",
"source": "",
"image": "https://avatars.githubusercontent.com/u/1082903?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,20 @@
version: "3.7"
services:
radarr:
image: lscr.io/linuxserver/sonarr
container_name: sonarr
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
dns:
- ${DNS_IP}
volumes:
- ${APP_DATA_DIR}/data:/config
- ${ROOT_FOLDER_HOST}/media/data/tv:/tv #optional
- ${ROOT_FOLDER_HOST}/media/torrents:/downloads #optional
ports:
- ${APP_PORT}:8989
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,13 @@
{
"name": "Syncthing",
"available": true,
"port": 8090,
"id": "syncthing",
"description": "Syncthing is a peer-to-peer continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.\n\nInstall the Syncthing app on your Umbrel and pair it with the Syncthing app on your phone or computer for a self hosted peer-to-peer backup solution.",
"short_desc": "Peer-to-peer file synchronization between your devices",
"author": "The Syncthing Foundation",
"source": "https://github.com/syncthing",
"website": "https://syncthing.net",
"image": "https://avatars.githubusercontent.com/u/7628018?s=200&v=4",
"form_fields": {}
}

View file

View file

@ -0,0 +1,21 @@
version: "3.7"
services:
syncthing:
container_name: syncthing
image: syncthing/syncthing:1.19
stop_grace_period: 1m
hostname: tipi
environment:
- PUID=1000
- PGID=1000
volumes:
- ${APP_DATA_DIR}/data:/var/syncthing
ports:
- ${APP_PORT}:8384
- 22000:22000/tcp # TCP file transfers
- 22000:22000/udp # QUIC file transfers
- 21027:21027/udp # Receive local discovery broadcasts
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,13 @@
{
"name": "Tailscale",
"available": true,
"port": 8093,
"id": "tailscale",
"description": "",
"short_desc": "",
"author": "",
"source": "https://github.com/tailscale/tailscale",
"website": "https://tailscale.com/",
"image": "https://avatars.githubusercontent.com/u/48932923?s=200&v=4",
"form_fields": {}
}

View file

@ -0,0 +1,14 @@
version: "2"
services:
tailscale:
container_name: tailscale
network_mode: "host" # TODO: Find a way to remove this
image: tailscale/tailscale:v1.24.0
privileged: true
restart: on-failure
stop_grace_period: 1m
command: "sh -c 'tailscale web --listen 0.0.0.0:${APP_PORT} & exec tailscaled --tun=userspace-networking'"
volumes:
- /var/lib:/var/lib
- /dev/net/tun:/dev/net/tun

View file

@ -0,0 +1,32 @@
{
"name": "Transmission",
"available": true,
"port": 8089,
"requirements": {
"ports": [51413]
},
"id": "transmission",
"description": "",
"short_desc": "",
"author": "",
"source": "https://transmissionbt.com",
"image": "https://avatars.githubusercontent.com/u/223312?s=200&v=4",
"form_fields": {
"username": {
"type": "text",
"label": "Username",
"max": 50,
"min": 3,
"required": true,
"env_variable": "TRANSMISSION_USERNAME"
},
"password": {
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "TRANSMISSION_PASSWORD"
}
}
}

View file

View file

@ -0,0 +1,24 @@
version: "3.7"
services:
transmission:
image: lscr.io/linuxserver/transmission
container_name: transmission
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
- USER=${TRANSMISSION_USERNAME}
- PASS=${TRANSMISSION_PASSWORD}
# - WHITELIST=iplist #optional
# - PEERPORT=peerport #optional
# - HOST_WHITELIST=dnsnane list #optional
volumes:
- ${APP_DATA_DIR}/data/config:/config
- ${ROOT_FOLDER_HOST}/media/torrents:/downloads
ports:
- ${APP_PORT}:9091
- 51413:51413
- 51413:51413/udp
restart: unless-stopped
networks:
- tipi_main_network

29
apps/ttyd/config.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "TTYD - Web terminal",
"port": 8092,
"id": "ttyd",
"description": "",
"short_desc": "A utility that allows you to access a command line from your web browser",
"author": "",
"source": "",
"image": "",
"form_fields": {
"username": {
"type": "text",
"label": "Username",
"max": 50,
"min": 3,
"required": true,
"env_variable": "TRANSMISSION_USERNAME"
},
"password": {
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "TRANSMISSION_PASSWORD"
}
}
}

View file

36
apps/wg-easy/config.json Normal file
View file

@ -0,0 +1,36 @@
{
"name": "Wireguard",
"available": true,
"port": 8082,
"requirements": {
"ports": [51820]
},
"id": "wg-easy",
"description": "Access your homeserver from anywhere even on your mobile device. Wireguard-easy is a simple tool to configure and manage Wireguard VPN servers. It is written in Go and uses the official Wireguard client. You have to open and redirect port 51820 to your homeserver in order to connect.",
"short_desc": "VPN server for your homeserver",
"author": "WeeJeWel",
"source": "https://github.com/WeeJeWel/wg-easy/",
"image": "https://avatars.githubusercontent.com/u/13991055?s=200&v=4",
"form_fields": {
"host": {
"type": "fqdnip",
"label": "Your public IP address or domain name",
"required": true,
"env_variable": "WIREGUARD_HOST"
},
"password": {
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "WIREGUARD_PASSWORD"
},
"dns": {
"type": "ip",
"label": "Default DNS server",
"required": false,
"env_variable": "WIREGUARD_DNS"
}
}
}

View file

View file

@ -0,0 +1,36 @@
version: "3.7"
services:
wg-easy:
container_name: wg-easy
image: "meienberger/wg-easy:latest"
restart: unless-stopped
volumes:
- ${APP_DATA_DIR}/data:/etc/wireguard
- /lib/modules:/lib/modules
ports:
- 51822:51820/udp
- ${APP_PORT}:51821/tcp
environment:
WG_HOST: "${WIREGUARD_HOST}"
PASSWORD: "${WIREGUARD_PASSWORD}"
WG_ALLOWED_IPS: 0.0.0.0/0,::/0
WG_PORT: 51822
WG_DEFAULT_DNS: "${WIREGUARD_DNS:-8.8.8.8}"
WG_FWMARK: 51820
cap_add:
- NET_ADMIN
- SYS_MODULE
dns:
- "${WIREGUARD_DNS:-8.8.8.8}"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
networks:
- tipi_main_network
# labels:
# traefik.enable: true
# traefik.http.routers.wireguard.rule: Host(`wireguard.tipi.home`)
# traefik.http.routers.wireguard.service: wireguard
# traefik.http.routers.wireguard.tls: true
# traefik.http.routers.wireguard.entrypoints: websecure
# traefik.http.services.wireguard.loadbalancer.server.port: 51821

View file

@ -1,12 +0,0 @@
ignore:
- 'public'
- 'scripts'
- 'templates'
- 'screenshots'
- '**/*.json'
- '**/tests/**'
coverage:
status:
project:
default:
informational: true

View file

@ -1,3 +0,0 @@
files:
- source: /src/client/messages/en.json
translation: /src/client/messages/%locale%.json

View file

@ -1,149 +1,55 @@
version: '3.7'
version: "3.7"
services:
tipi-reverse-proxy:
container_name: tipi-reverse-proxy
image: traefik:v2.8
restart: on-failure
ports:
- 80:80
- 443:443
- 8080:8080
command: --providers.docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}/traefik:/root/.config
- ${PWD}/traefik/shared:/shared
networks:
- tipi_main_network
tipi-db:
container_name: tipi-db
image: postgres:14
restart: unless-stopped
stop_grace_period: 1m
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_USER: tipi
POSTGRES_DB: tipi
healthcheck:
test: ['CMD-SHELL', 'pg_isready -d tipi -U tipi']
interval: 5s
timeout: 10s
retries: 120
networks:
- tipi_main_network
tipi-redis:
container_name: tipi-redis
image: redis:7.2.0
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- 6379:6379
volumes:
- redisdata:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 10s
retries: 120
networks:
- tipi_main_network
tipi-worker:
api:
build:
context: .
dockerfile: ./packages/worker/Dockerfile.dev
container_name: tipi-worker
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthcheck']
interval: 5s
timeout: 10s
retries: 120
start_period: 5s
depends_on:
tipi-db:
condition: service_healthy
tipi-redis:
condition: service_healthy
env_file:
- .env
environment:
NODE_ENV: development
volumes:
# Dev mode
- ${PWD}/packages/worker/src:/app/packages/worker/src
# Production mode
- /proc:/host/proc:ro
- /var/run/docker.sock:/var/run/docker.sock
- ${PWD}/.env:/app/.env
- ${PWD}/state:/app/state
- ${PWD}/repos:/app/repos
- ${PWD}/apps:/app/apps
- ${STORAGE_PATH:-$PWD}/app-data:/storage/app-data
- ${PWD}/logs:/app/logs
- ${PWD}/traefik:/app/traefik
- ${PWD}/user-config:/app/user-config
networks:
- tipi_main_network
tipi-dashboard:
build:
context: .
context: ./packages/system-api
dockerfile: Dockerfile.dev
container_name: tipi-dashboard
depends_on:
tipi-db:
condition: service_healthy
tipi-redis:
condition: service_healthy
tipi-worker:
condition: service_healthy
env_file:
- .env
container_name: api
ports:
- 3001:3001
volumes:
## Docker sock
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}:/tipi
- ${PWD}/packages/system-api:/app
- /app/node_modules
environment:
NODE_ENV: development
- INTERNAL_IP=${INTERNAL_IP}
- TIPI_VERSION=${TIPI_VERSION}
- JWT_SECRET=${JWT_SECRET}
- ROOT_FOLDER_HOST=${ROOT_FOLDER_HOST}
networks:
- tipi_main_network
dashboard:
build:
context: ./packages/dashboard
dockerfile: Dockerfile.dev
container_name: dashboard
ports:
- 3000:3000
networks:
- tipi_main_network
environment:
- INTERNAL_IP=${INTERNAL_IP}
volumes:
# - /dashboard/node_modules
# - /dashboard/.next
- ${PWD}/.env:/runtipi/.env
- ${PWD}/src:/app/src
- ${PWD}/packages:/app/packages
- ${PWD}/state:/runtipi/state
- ${PWD}/repos:/runtipi/repos:ro
- ${PWD}/apps:/runtipi/apps
- ${PWD}/logs:/app/logs
- ${PWD}/traefik:/runtipi/traefik
- ${STORAGE_PATH:-$PWD}:/app/storage
- ${PWD}/packages/dashboard:/app
- /app/node_modules
labels:
traefik.enable: true
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
traefik.http.routers.dashboard.entrypoints: webinsecure
traefik.http.routers.dashboard.service: dashboard
traefik.http.services.dashboard.loadbalancer.server.port: 3000
traefik.http.middlewares.redirect-to-https.redirectscheme.scheme: https
# Local domain
traefik.http.routers.dashboard-local-insecure.rule: Host(`${LOCAL_DOMAIN}`)
traefik.http.routers.dashboard-local-insecure.entrypoints: web
traefik.http.routers.dashboard-local-insecure.service: dashboard
traefik.http.routers.dashboard-local-insecure.middlewares: redirect-to-https
# secure
traefik.http.routers.dashboard-local.rule: Host(`${LOCAL_DOMAIN}`)
traefik.http.routers.dashboard-local.entrypoints: websecure
traefik.http.routers.dashboard-local.tls: true
traefik.http.routers.dashboard-local.service: dashboard
networks:
tipi_main_network:
driver: bridge
name: runtipi_tipi_main_network
volumes:
pgdata:
redisdata:
driver_opts:
com.docker.network.bridge.enable_ip_masquerade: "true"
com.docker.network.bridge.enable_icc: "true"
ipam:
driver: default
config:
- subnet: 10.21.21.0/24

View file

@ -1,146 +0,0 @@
version: '3.7'
services:
tipi-reverse-proxy:
container_name: tipi-reverse-proxy
image: traefik:v2.8
restart: on-failure
ports:
- 80:80
- 443:443
- 8080:8080
command: --providers.docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}/traefik:/root/.config
- ${PWD}/traefik/shared:/shared
networks:
- tipi_main_network
tipi-db:
container_name: tipi-db
image: postgres:14
restart: unless-stopped
stop_grace_period: 1m
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_USER: tipi
POSTGRES_DB: tipi
healthcheck:
test: ['CMD-SHELL', 'pg_isready -d tipi -U tipi']
interval: 5s
timeout: 10s
retries: 120
networks:
- tipi_main_network
tipi-redis:
container_name: tipi-redis
image: redis:7.2.0
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- 6379:6379
volumes:
- redisdata:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 10s
retries: 120
networks:
- tipi_main_network
tipi-worker:
build:
context: .
dockerfile: ./packages/worker/Dockerfile
container_name: tipi-worker
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthcheck']
interval: 5s
timeout: 10s
retries: 120
start_period: 5s
depends_on:
tipi-db:
condition: service_healthy
tipi-redis:
condition: service_healthy
env_file:
- .env
environment:
NODE_ENV: production
volumes:
- /proc:/host/proc
- /var/run/docker.sock:/var/run/docker.sock
- ${PWD}/.env:/app/.env
- ${PWD}/state:/app/state
- ${PWD}/repos:/app/repos
- ${PWD}/apps:/app/apps
- ${STORAGE_PATH:-$PWD}/app-data:/storage/app-data
- ${PWD}/logs:/app/logs
- ${PWD}/traefik:/app/traefik
- ${PWD}/user-config:/app/user-config
networks:
- tipi_main_network
tipi-dashboard:
build:
context: .
dockerfile: Dockerfile
container_name: tipi-dashboard
depends_on:
tipi-db:
condition: service_healthy
tipi-redis:
condition: service_healthy
tipi-worker:
condition: service_healthy
env_file:
- .env
environment:
NODE_ENV: development
networks:
- tipi_main_network
ports:
- 3000:3000
volumes:
- ${PWD}/.env:/runtipi/.env
- ${PWD}/state:/runtipi/state
- ${PWD}/repos:/runtipi/repos:ro
- ${PWD}/apps:/runtipi/apps
- ${PWD}/logs:/app/logs
- ${PWD}/traefik:/runtipi/traefik
- ${STORAGE_PATH}:/app/storage
labels:
traefik.enable: true
traefik.http.services.dashboard.loadbalancer.server.port: 3000
traefik.http.middlewares.redirect-to-https.redirectscheme.scheme: https
# Local ip
traefik.http.routers.dashboard.rule: PathPrefix("/")
traefik.http.routers.dashboard.service: dashboard
traefik.http.routers.dashboard.entrypoints: web
# Local domain
traefik.http.routers.dashboard-local-insecure.rule: Host(`${LOCAL_DOMAIN}`)
traefik.http.routers.dashboard-local-insecure.entrypoints: web
traefik.http.routers.dashboard-local-insecure.service: dashboard
traefik.http.routers.dashboard-local-insecure.middlewares: redirect-to-https
# secure
traefik.http.routers.dashboard-local.rule: Host(`${LOCAL_DOMAIN}`)
traefik.http.routers.dashboard-local.entrypoints: websecure
traefik.http.routers.dashboard-local.tls: true
traefik.http.routers.dashboard-local.service: dashboard
networks:
tipi_main_network:
driver: bridge
name: runtipi_tipi_main_network
volumes:
pgdata:
redisdata:

60
docker-compose.rc.yml Normal file
View file

@ -0,0 +1,60 @@
version: "3.7"
services:
reverse-proxy:
container_name: reverse-proxy
image: traefik:v2.6
restart: always
ports:
- ${NGINX_PORT}:80
- ${PROXY_PORT}:8080
command: --api.insecure=true --providers.docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}/traefik:/root/.config
networks:
- tipi_main_network
api:
image: meienberger/tipi-api:rc-${TIPI_VERSION}
container_name: api
ports:
- 3001:3001
volumes:
## Docker sock
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}:/tipi
environment:
- INTERNAL_IP=${INTERNAL_IP}
- TIPI_VERSION=${TIPI_VERSION}
- JWT_SECRET=${JWT_SECRET}
- ROOT_FOLDER_HOST=${ROOT_FOLDER_HOST}
networks:
- tipi_main_network
dashboard:
image: meienberger/tipi-dashboard:rc-${TIPI_VERSION}
container_name: dashboard
ports:
- 3000:3000
networks:
- tipi_main_network
environment:
- INTERNAL_IP=${INTERNAL_IP}
labels:
traefik.enable: true
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
traefik.http.routers.dashboard.entrypoints: webinsecure
traefik.http.routers.dashboard.service: dashboard
traefik.http.services.dashboard.loadbalancer.server.port: 3000
networks:
tipi_main_network:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_ip_masquerade: "true"
com.docker.network.bridge.enable_icc: "true"
ipam:
driver: default
config:
- subnet: 10.21.21.0/24

Some files were not shown because too many files have changed in this diff Show more