Compare commits

..

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

787 changed files with 18590 additions and 36291 deletions

View file

@ -36,11 +36,7 @@
"contributions": [
"code",
"ideas",
"test",
"content",
"promotion",
"question",
"review"
"test"
]
},
{
@ -69,345 +65,12 @@
"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",
"projectOwner": "meienberger",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true,
"commitConvention": "angular",
"commitType": "docs"
"skipCi": true
}

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

@ -5,22 +5,5 @@
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__/
**/dist/
**/next/

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,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,35 +1,22 @@
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
image: postgres:latest
env:
POSTGRES_PASSWORD: postgres
ports:
- 5433:5432
- 5432:5432
# set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
@ -38,24 +25,24 @@ jobs:
--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 +55,19 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Build packages
run: pnpm -r build
- name: Run global tests
run: pnpm test
- 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
run: pnpm -r lint
- 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
- uses: codecov/codecov-action@v2
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
files: ./packages/system-api/coverage/clover.xml,./packages/dashboard/coverage/clover.xml

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

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

@ -0,0 +1,43 @@
name: Release candidate
on:
push:
branches:
- release/*
jobs:
# Build images and publish RCs to DockerHub
build-images:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get tag from VERSION file
id: meta
run: |
VERSION=$(npm run version --silent)
TAG=${VERSION}
echo "::set-output name=tag::${TAG}"
- name: Build and push images
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: meienberger/runtipi:rc-${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/runtipi:buildcache
cache-to: type=registry,ref=meienberger/runtipi:buildcache,mode=max

View file

@ -1,192 +1,61 @@
name: Publish release
on:
workflow_dispatch:
push:
branches:
- master
jobs:
create-tag:
runs-on: ubuntu-latest
needs: [build-images, build-cli]
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}" >> $GITHUB_OUTPUT
- uses: rickstaa/action-create-tag@v1
with:
tag: ${{ steps.get_tag.outputs.tagname }}
build-images:
if: github.repository == 'runtipi/runtipi'
needs: create-tag
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Get tag from VERSION file
id: meta
run: |
VERSION=$(npm run version --silent)
TAG=${VERSION}
echo "::set-output name=tag::${TAG}"
- name: Build and push images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64,linux/arm/v7
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
tags: meienberger/runtipi:latest,meienberger/runtipi:${{ steps.meta.outputs.TAG }}
cache-from: type=registry,ref=meienberger/runtipi:buildcache
cache-to: type=registry,ref=meienberger/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
- name: Create Tag
id: create_tag
uses: jaywcjlove/create-tag-action@v1.1.5
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
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]
runs-on: ubuntu-latest
steps:
- name: Promote release
uses: actions/github-script@v7
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
});
prerelease: false

85
.gitignore vendored
View file

@ -1,69 +1,30 @@
*.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.example
!.env.test
data/
github.secrets
node_modules/
/app-data/
/data/
/repos/
/apps/
/traefik/
app-data/*
data/
traefik/ssl/*
!traefik/ssl/.gitkeep
!app-data/.gitkeep
# media folder
media
scripts/pacapt
/state/
/test-results/
/playwright-report/
/playwright/.cache/
temp
state/*
!state/.gitkeep
./traefik/
/user-config/
media/data/movies/*
media/data/tv/*
media/data/books/*
!media/data/movies/.gitkeep
!media/data/tv/.gitkeep
!media/data/books/metadata.db
media/torrents/complete/*
!media/torrents/complete/.gitkeep
media/torrents/incomplete/*
!media/torrents/incomplete/.gitkeep
media/torrents/watch/*
!media/torrents/watch/.gitkeep
packages/dashboard/package-lock.json

6
.husky/pre-commit Executable file
View file

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

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 +1,45 @@
ARG NODE_VERSION="20.10"
ARG ALPINE_VERSION="3.18"
FROM node:18 AS build
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS node_base
RUN npm install node-gyp -g
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
WORKDIR /api
COPY ./packages/system-api/package.json /api/package.json
RUN npm i
# ---
WORKDIR /dashboard
COPY ./packages/dashboard/package.json /dashboard/package.json
RUN npm i
WORKDIR /api
COPY ./packages/system-api /api
RUN npm run build
# ---
WORKDIR /dashboard
COPY ./packages/dashboard /dashboard
RUN npm run build
# APP
FROM node_base AS app
ENV NODE_ENV production
FROM alpine:3.16.0 as app
USER node
WORKDIR /
WORKDIR /app
# Install dependencies
RUN apk --no-cache add docker-compose nodejs npm bash g++ make
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
RUN npm install node-gyp -g
EXPOSE 3000
WORKDIR /api
COPY ./packages/system-api/package*.json /api/
RUN npm install --production
CMD ["npm", "run", "start"]
WORKDIR /dashboard
COPY ./packages/dashboard/package*.json /dashboard/
RUN npm install --production
COPY --from=build /api/dist /api/dist
COPY ./packages/system-api /api
COPY --from=build /dashboard/.next /dashboard/.next
COPY ./packages/dashboard /dashboard
WORKDIR /

View file

@ -1,23 +1,21 @@
ARG NODE_VERSION="20.10"
ARG ALPINE_VERSION="3.18"
FROM alpine:3.16.0 as app
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION}
WORKDIR /
RUN npm install pnpm -g
# Install docker
RUN apk --no-cache add docker-compose nodejs npm bash
WORKDIR /app
RUN npm install node-gyp -g
COPY ./pnpm-lock.yaml ./
COPY ./patches ./patches
RUN pnpm fetch --ignore-scripts
WORKDIR /api
COPY ./packages/system-api/package*.json /api/
RUN npm install
COPY ./package*.json ./
COPY ./packages/shared ./packages/shared
WORKDIR /dashboard
COPY ./packages/dashboard/package*.json /dashboard/
RUN npm install
RUN pnpm install -r --prefer-offline
COPY ./packages/system-api /api
COPY ./packages/dashboard /dashboard
COPY ./tsconfig.json ./tsconfig.json
COPY ./next.config.mjs ./next.config.mjs
COPY ./public ./public
CMD ["npm", "run", "dev"]
WORKDIR /

167
README.md
View file

@ -1,63 +1,102 @@
# Tipi — A personal homeserver for everyone
# ⛺️ 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](https://img.shields.io/badge/all_contributors-6-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)
[![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/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!
![Build](https://github.com/meienberger/runtipi/workflows/Tipi%20CI/badge.svg)
#### 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)
![Preview](https://raw.githubusercontent.com/meienberger/runtipi/develop/screenshots/appstore.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
Check our demo instance : **95.179.210.152** / username: **user@runtipi.com** / password: **runtipi**
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.
## Apps available
- [Adguard Home](https://github.com/AdguardTeam/AdGuardHome) - Adguard Home DNS adblocker
- [Calibre-Web](https://github.com/janeczku/calibre-web) - Web Ebook Reader
- [Code-Server](https://github.com/coder/code-server) - Web VS Code
- [Filebrowser](https://github.com/filebrowser/filebrowser) - Web File Browser
- [Freshrss](https://github.com/FreshRSS/FreshRSS) - A free, self-hostable RSS aggregator
- [Gitea](https://github.com/go-gitea/gitea) - Gitea - A painless self-hosted Git service
- [Homarr](https://github.com/ajnart/homarr) - A homepage for your server
- [Home Assistant](https://github.com/home-assistant/core) - Open source home automation that puts local control and privacy first
- [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
- [Libreddit](https://github.com/spikecodes/libreddit) - Private front-end for Reddit
- [Mealie](https://github.com/hay-kot/mealie) - Self-hosted recipe manager and meal planner.
- [n8n](https://github.com/n8n-io/n8n) - Workflow Automation Tool
- [Nextcloud](https://github.com/nextcloud/server) - A safe home for all your data
- [Nitter](https://github.com/zedeus/nitter) - Alternative Twitter front-end
- [Node-RED](https://github.com/node-red/node-red) - Low-code programming for event-driven applications
- [Photoprism](https://github.com/photoprism/photoprism) - AI-Powered Photos App for the Decentralized Web. We are on a mission to protect your freedom and privacy.
- [Pihole](https://github.com/pi-hole/pi-hole) - A black hole for Internet advertisements
- [Plex](https://github.com/plexinc/pms-docker) - Stream Movies & TV Shows
- [Prowlarr](https://github.com/Prowlarr/Prowlarr/) - A torrent/usenet indexer manager/proxy
- [Radarr](https://github.com/Radarr/Radarr) - Movie collection manager for Usenet and BitTorrent users
- [Resilio Sync](https://github.com/bt-sync) - Fast, reliable, and simple file sync and share solution
- [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
- [Tautulli](https://github.com/Tautulli/Tautulli) - A Python based monitoring and tracking tool for Plex Media Server
- [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
- [Vaultwarden](https://github.com/dani-garcia/vaultwarden) - Unofficial Bitwarden compatible server
## Demo
## 🛠 Installation
You can try out a demo of Tipi at [demo.runtipi.io](https://demo.runtipi.io) using the following credentials:
### Installation Requirements
Ubuntu 18.04 LTS or higher is recommended. However other major Linux distribution are supported but may lead to installation issues. Please file an issue if you encounter one.
username: user@runtipi.io
password: password
### Step 1. Download Tipi
Run this in an empty directory where you want to install Tipi.
## 📚 Documentation
```bash
git clone https://github.com/meienberger/runtipi.git
```
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/)
### Step 2. Run Tipi
cd into the downloaded directory and run the start script.
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)
```bash
cd runtipi
sudo ./scripts/start.sh
```
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
```bash
sudo ./scripts/start.sh --port 7000
```
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 add a new app or feature, you can follow the [Contribution guide](https://github.com/meienberger/runtipi/wiki/Contributing-to-Tipi) for instructions on how to do so.
## 📜 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)
@ -71,62 +110,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- 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>
<tr>
<td align="center"><a href="https://meienberger.dev/"><img src="https://avatars.githubusercontent.com/u/47644445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nicolas Meienberger</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=meienberger" title="Code">💻</a> <a href="#infra-meienberger" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/meienberger/runtipi/commits?author=meienberger" title="Tests">⚠️</a> <a href="https://github.com/meienberger/runtipi/commits?author=meienberger" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/ArneNaessens"><img src="https://avatars.githubusercontent.com/u/16622722?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ArneNaessens</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=ArneNaessens" title="Code">💻</a> <a href="#ideas-ArneNaessens" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/meienberger/runtipi/commits?author=ArneNaessens" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/DrMxrcy"><img src="https://avatars.githubusercontent.com/u/58747968?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DrMxrcy</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=DrMxrcy" title="Code">💻</a> <a href="#ideas-DrMxrcy" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/meienberger/runtipi/commits?author=DrMxrcy" title="Tests">⚠️</a></td>
<td align="center"><a href="https://cobre.dev"><img src="https://avatars.githubusercontent.com/u/36574329?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cooper</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=CobreDev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JTruj1ll0923"><img src="https://avatars.githubusercontent.com/u/6656643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JTruj1ll0923</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=JTruj1ll0923" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Stetsed"><img src="https://avatars.githubusercontent.com/u/33891782?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stetsed</b></sub></a><br /><a href="https://github.com/meienberger/runtipi/commits?author=Stetsed" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
@ -134,4 +125,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

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;
},
};
});

176
apps/__tests__/apps.test.ts Normal file
View file

@ -0,0 +1,176 @@
import fs from "fs";
import jsyaml from "js-yaml";
interface AppConfig {
id: string;
port: number;
categories: string[];
requirements?: {
ports?: number[];
};
name: string;
description: string;
version: string;
image: string;
short_desc: string;
author: string;
source: string;
available: boolean;
}
const networkExceptions = ["pihole", "tailscale", "homeassistant", "plex"];
const getAppConfigs = (): AppConfig[] => {
const apps: AppConfig[] = [];
const appsDir = fs.readdirSync("./apps");
appsDir.forEach((app) => {
const path = `./apps/${app}/config.json`;
if (fs.existsSync(path)) {
const configFile = fs.readFileSync(path).toString();
try {
const config: AppConfig = JSON.parse(configFile);
if (config.available) {
apps.push(config);
}
} catch (e) {
console.error("Error parsing config file", app);
}
}
});
return apps;
};
describe("App configs", () => {
it("Get app config should return at least one app", () => {
const apps = getAppConfigs();
expect(apps.length).toBeGreaterThan(0);
});
it("Each app should have an id", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.id).toBeDefined();
});
});
it("Each app should have a md description", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
const path = `./apps/${app.id}/metadata/description.md`;
if (fs.existsSync(path)) {
const description = fs.readFileSync(path).toString();
expect(description).toBeDefined();
} else {
console.error(`Missing description for app ${app.id}`);
expect(true).toBe(false);
}
});
});
it("Each app should have categories defined as an array", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.categories).toBeDefined();
expect(app.categories).toBeInstanceOf(Array);
});
});
it("Each app should have a name", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.name).toBeDefined();
});
});
it("Each app should have a description", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.description).toBeDefined();
});
});
it("Each app should have a port", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.port).toBeDefined();
expect(app.port).toBeGreaterThan(999);
expect(app.port).toBeLessThan(65535);
});
});
it("Each app should have a different port", () => {
const appConfigs = getAppConfigs();
const ports = appConfigs.map((app) => app.port);
expect(new Set(ports).size).toBe(appConfigs.length);
});
it("Each app should have a unique id", () => {
const appConfigs = getAppConfigs();
const ids = appConfigs.map((app) => app.id);
expect(new Set(ids).size).toBe(appConfigs.length);
});
it("Each app should have a docker-compose file beside it", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(fs.existsSync(`./apps/${app.id}/docker-compose.yml`)).toBe(true);
});
});
it("Each app should have a container name equals to its id", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
const dockerComposeFile = fs
.readFileSync(`./apps/${app.id}/docker-compose.yml`)
.toString();
const dockerCompose: any = jsyaml.load(dockerComposeFile);
if (!dockerCompose.services[app.id]) {
console.error(app.id);
}
expect(dockerCompose.services[app.id]).toBeDefined();
expect(dockerCompose.services[app.id].container_name).toBe(app.id);
});
});
it("Each app should have network tipi_main_network", () => {
const apps = getAppConfigs();
apps.forEach((app) => {
if (!networkExceptions.includes(app.id)) {
const dockerComposeFile = fs
.readFileSync(`./apps/${app.id}/docker-compose.yml`)
.toString();
const dockerCompose: any = jsyaml.load(dockerComposeFile);
expect(dockerCompose.services[app.id]).toBeDefined();
if (!dockerCompose.services[app.id].networks) {
console.error(app.id);
}
expect(dockerCompose.services[app.id].networks).toBeDefined();
expect(dockerCompose.services[app.id].networks).toStrictEqual([
"tipi_main_network",
]);
}
});
});
});

16
apps/adguard/config.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "Adguard",
"available": true,
"port": 8104,
"id": "adguard",
"categories": ["network", "security"],
"description": "Adguard is the best way to get rid of annoying ads and online tracking and protect your computer from malware. Make your web surfing fast, safe and ad-free.",
"short_desc": "World's most advanced adblocker!",
"author": "AdguardTeam",
"source": "https://github.com/AdguardTeam",
"image": "/logos/apps/adguard.jpg",
"requirements": {
"ports": [53]
},
"form_fields": []
}

View file

@ -0,0 +1,131 @@
bind_host: 0.0.0.0
bind_port: 80
beta_bind_port: 0
users: []
auth_attempts: 5
block_auth_min: 15
http_proxy: ""
language: ""
debug_pprof: false
web_session_ttl: 720
dns:
bind_hosts:
- 0.0.0.0
port: 53
statistics_interval: 1
querylog_enabled: true
querylog_file_enabled: true
querylog_interval: 2160h
querylog_size_memory: 1000
anonymize_client_ip: false
protection_enabled: true
blocking_mode: default
blocking_ipv4: ""
blocking_ipv6: ""
blocked_response_ttl: 10
parental_block_host: family-block.dns.adguard.com
safebrowsing_block_host: standard-block.dns.adguard.com
ratelimit: 20
ratelimit_whitelist: []
refuse_any: true
upstream_dns:
- https://dns10.quad9.net/dns-query
upstream_dns_file: ""
bootstrap_dns:
- 9.9.9.10
- 149.112.112.10
- 2620:fe::10
- 2620:fe::fe:10
all_servers: false
fastest_addr: false
fastest_timeout: 1s
allowed_clients: []
disallowed_clients: []
blocked_hosts:
- version.bind
- id.server
- hostname.bind
trusted_proxies:
- 127.0.0.0/8
- ::1/128
cache_size: 4194304
cache_ttl_min: 0
cache_ttl_max: 0
cache_optimistic: false
bogus_nxdomain: []
aaaa_disabled: false
enable_dnssec: false
edns_client_subnet: false
max_goroutines: 300
ipset: []
filtering_enabled: true
filters_update_interval: 24
parental_enabled: false
safesearch_enabled: false
safebrowsing_enabled: false
safebrowsing_cache_size: 1048576
safesearch_cache_size: 1048576
parental_cache_size: 1048576
cache_time: 30
rewrites: []
blocked_services: []
upstream_timeout: 10s
local_domain_name: lan
resolve_clients: true
use_private_ptr_resolvers: true
local_ptr_upstreams: []
tls:
enabled: false
server_name: ""
force_https: false
port_https: 443
port_dns_over_tls: 853
port_dns_over_quic: 784
port_dnscrypt: 0
dnscrypt_config_file: ""
allow_unencrypted_doh: false
strict_sni_check: false
certificate_chain: ""
private_key: ""
certificate_path: ""
private_key_path: ""
filters:
- enabled: true
url: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
name: AdGuard DNS filter
id: 1
- enabled: false
url: https://adaway.org/hosts.txt
name: AdAway Default Blocklist
id: 2
whitelist_filters: []
user_rules: []
dhcp:
enabled: false
interface_name: ""
dhcpv4:
gateway_ip: ""
subnet_mask: ""
range_start: ""
range_end: ""
lease_duration: 86400
icmp_timeout_msec: 1000
options: []
dhcpv6:
range_start: ""
lease_duration: 86400
ra_slaac_only: false
ra_allow_slaac: false
clients: []
log_compress: false
log_localtime: false
log_max_backups: 0
log_max_size: 100
log_max_age: 3
log_file: ""
verbose: false
os:
group: ""
user: ""
rlimit_nofile: 0
schema_version: 12

View file

@ -0,0 +1,16 @@
version: "3.7"
services:
adguard:
image: adguard/adguardhome:v0.107.7
container_name: adguard
volumes:
- "${APP_DATA_DIR}/data/work:/opt/adguardhome/work"
- "${APP_DATA_DIR}/data/conf:/opt/adguardhome/conf"
restart: unless-stopped
networks:
- tipi_main_network
ports:
- 53:53/tcp
- 53:53/udp
- ${APP_PORT}:80

View file

@ -0,0 +1,6 @@
## AdGuard Home is a network-wide software for blocking ads & tracking.
After you set it up, it'll cover ALL your home devices, and you don't need any client-side software for that.
It operates as a DNS server that re-routes tracking domains to a "black hole", thus preventing your devices from connecting to those servers. It's based on software we use for our public AdGuard DNS servers -- both share a lot of common code.
![Screenshot](https://cdn.adguard.com/public/Adguard/Common/adguard_home.gif)

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

@ -0,0 +1,47 @@
{
"name": "Anonaddy",
"port": 8084,
"available": false,
"categories": ["utilities"],
"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": [
{
"type": "text",
"label": "Username",
"required": true,
"env_variable": "ANONADDY_USERNAME"
},
{
"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"
},
{
"type": "fqdn",
"label": "Your email domain (eg. example.com)",
"max": 50,
"min": 3,
"required": true,
"env_variable": "ANONADDY_DOMAIN"
},
{
"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,80 @@
version: "3.7"
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}
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,13 @@
{
"name": "Calibre-Web - EBook Reader",
"available": true,
"port": 8100,
"id": "calibre-web",
"categories": ["books"],
"description": "On the initial setup screen, enter /books as your calibre library location. \n Default admin login: Username: admin Password: admin123",
"short_desc": "Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database.",
"author": "https://github.com/janeczku/",
"source": "https://github.com/janeczku/calibre-web",
"image": "/logos/apps/calibre-web.jpg",
"form_fields": []
}

Binary file not shown.

View file

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

View file

@ -0,0 +1,7 @@
Calibre-Web is a web app providing a clean interface for browsing, reading and downloading eBooks using a valid Calibre database.
On the initial setup screen, enter /books as your calibre library location.
- **username**: admin
- **password**: admin123
<br />

View file

@ -0,0 +1,22 @@
{
"name": "Code-Server - Web VS Code",
"available": true,
"port": 8101,
"id": "code-server",
"categories": ["development"],
"description": "",
"short_desc": "Code-server is VS Code running on a remote server, accessible through the browser.",
"author": "https://github.com/coder",
"source": "https://github.com/linuxserver/docker-code-server",
"image": "https://avatars.githubusercontent.com/u/95932066",
"form_fields": [
{
"type": "password",
"label": "Password",
"max": 50,
"min": 3,
"required": true,
"env_variable": "CODESERVER_PASSWORD"
}
]
}

View file

@ -0,0 +1,19 @@
version: "3.7"
services:
code-server:
image: lscr.io/linuxserver/code-server:4.5.1
container_name: code-server
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
- PASSWORD=${CODESERVER_PASSWORD}
- DEFAULT_WORKSPACE=/config/workspace #optional
volumes:
- ${APP_DATA_DIR}/data/config:/config #config dir
- ${APP_DATA_DIR}/data/projects:/projects
ports:
- ${APP_PORT}:8443
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,7 @@
## Run VS Code on any machine anywhere and access it in the browser.
- Code on any device with a consistent development environment
- Use cloud servers to speed up tests, compilations, downloads, and more
- Preserve battery life when you're on the go; all intensive tasks run on your server
![screenshot](https://raw.githubusercontent.com/coder/code-server/main/docs/assets/screenshot.png)

View file

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

View file

@ -0,0 +1,14 @@
{
"name": "File Browser",
"available": true,
"port": 8096,
"id": "filebrowser",
"categories": ["utilities"],
"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": "filebrowser.org",
"website": "https://filebrowser.org/",
"source": "https://github.com/filebrowser/filebrowser",
"image": "/logos/apps/filebrowser.jpg",
"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,17 @@
version: "3.7"
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

View file

@ -0,0 +1,12 @@
## Access your homeserver files from your browser
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app.
- **username**: admin
- **password**: admin
<br />
## Features
Please refer to our docs at [https://filebrowser.org/features](https://filebrowser.org/features)
![Preview](https://user-images.githubusercontent.com/5447088/50716739-ebd26700-107a-11e9-9817-14230c53efd2.gif)

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

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

@ -0,0 +1,13 @@
{
"name": "FreshRSS",
"available": true,
"port": 8086,
"id": "freshrss",
"categories": ["utilities"],
"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": "/logos/apps/freshrss.jpg",
"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,27 @@
## A free, self-hostable aggregator…
FreshRSS is a self-hosted RSS feed aggregator like [Leed](https://github.com/LeedRSS/Leed) or [Kriss Feed](https://tontof.net/kriss/feed/).
It is lightweight, easy to work with, powerful, and customizable.
It 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](cli/README.md).
Thanks to the [WebSub](https://www.w3.org/TR/websub/) standard (formerly [PubSubHubbub](https://github.com/pubsubhubbub/PubSubHubbub)),
FreshRSS is able to receive instant push notifications from compatible sources, such as [Mastodon](https://joinmastodon.org), [Friendica](https://friendi.ca), [WordPress](https://wordpress.org/plugins/pubsubhubbub/), Blogger, FeedBurner, etc.
FreshRSS natively supports basic Web scraping, based on [XPath](https://www.w3.org/TR/xpath-10/), for Web sites not providing any RSS / Atom feed.
Finally, it supports [extensions](#extensions) for further tuning.
Feature requests, bug reports, and other contributions are welcome. The best way to contribute is to [open an issue on GitHub](https://github.com/FreshRSS/FreshRSS/issues).
We are a friendly community.
* Official website: [website](https://freshrss.org)
* Demo: [Demo](https://demo.freshrss.org/)
* License: [GNU AGPL 3](https://www.gnu.org/licenses/agpl-3.0.html)
![Screenshot](https://raw.githubusercontent.com/FreshRSS/FreshRSS/edge/docs/img/FreshRSS-screenshot.png)

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

@ -0,0 +1,13 @@
{
"name": "Gitea",
"port": 8108,
"available": true,
"id": "gitea",
"categories": ["development"],
"description": "Gitea is a painless self-hosted Git service. It is similar to GitHub, Bitbucket, and GitLab. Gitea is a fork of Gogs. See the Gitea Announcement blog post to read about the justification for a fork.",
"short_desc": "Gitea - Git with a cup of tea · A painless self-hosted Git service. · Cross-platform · Easy to install · Lightweight · Open Source.",
"author": "go-gitea",
"source": "https://github.com/go-gitea/gitea",
"image": "/logos/apps/gitea.jpg",
"form_fields": []
}

View file

@ -0,0 +1,88 @@
APP_NAME = Gitea: Git with a cup of tea
RUN_MODE = prod
RUN_USER = git
[repository]
ROOT = /data/git/repositories
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = localhost
SSH_DOMAIN = localhost
HTTP_PORT = 3000
ROOT_URL = http://localhost:8108/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_CONTENT_PATH = /data/git/lfs
LFS_JWT_SECRET = wo2G20l0nGsspUp8xsLNSNF7H8U-GQUVth5gj_q5cDk
OFFLINE_MODE = false
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = postgres
HOST = gitea-db:5432
NAME = gitea
USER = gitea
PASSWD = gitea
LOG_SQL = false
SCHEMA =
SSL_MODE = disable
CHARSET = utf8
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
[session]
PROVIDER_CONFIG = /data/gitea/sessions
PROVIDER = file
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = true
[attachment]
PATH = /data/gitea/attachments
[log]
MODE = console
LEVEL = info
ROUTER = console
ROOT_PATH = /data/gitea/log
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE2NTMyODU5ODh9.l7fPuVA8LSHZvdBum8YDrH47RZjEx_cZLbswO5pMDk8
PASSWORD_HASH_ALGO = pbkdf2
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
[mailer]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true

View file

@ -0,0 +1,37 @@
version: "3.7"
services:
gitea:
image: gitea/gitea:1.16.8
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea-db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
restart: unless-stopped
networks:
- tipi_main_network
volumes:
- ${APP_DATA_DIR}/data/gitea:/data
ports:
- ${APP_PORT}:3000
- "222:22"
depends_on:
- gitea-db
gitea-db:
container_name: gitea-db
image: postgres:14
restart: unless-stopped
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
volumes:
- ${APP_DATA_DIR}/data/postgres:/var/lib/postgresql/data
networks:
- tipi_main_network

View file

@ -0,0 +1,5 @@
# A painless self-hosted Git service
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. Using Go, this can be done with an independent binary distribution across all platforms which Go supports, including Linux, macOS, and Windows on x86, amd64, ARM and PowerPC architectures. Want to try it before doing anything else? Do it with the online demo! This project has been forked from Gogs since 2016.11 but changed a lot.
![Screenshot](https://camo.githubusercontent.com/134f49a62801ab393c211125515d108439c67b59befb2ec3e42e7235af4f5d6c/68747470733a2f2f646c2e67697465612e696f2f73637265656e73686f74732f686f6d655f74696d656c696e652e706e67)

14
apps/homarr/config.json Normal file
View file

@ -0,0 +1,14 @@
{
"name": "Homarr",
"available": true,
"port": 8102,
"id": "homarr",
"categories": ["utilities"],
"description": "A homepage for your server.",
"short_desc": "Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.",
"author": "ajnart",
"source": "https://github.com/ajnart/homarr",
"website": "https://discord.gg/C2WTXkzkwK",
"image": "/logos/apps/homarr.jpg",
"form_fields": []
}

View file

@ -0,0 +1,12 @@
version: "3.7"
services:
homarr:
container_name: homarr
image: ghcr.io/ajnart/homarr:0.8.0
restart: unless-stopped
volumes:
- ${APP_DATA_DIR}/data/config:/app/data/configs
ports:
- ${APP_PORT}:7575
networks:
- tipi_main_network

View file

@ -0,0 +1,10 @@
# Lightweight homepage for your server
Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.
It integrates with the services you use to display information on the homepage (E.g. Show upcoming Sonarr/Radarr releases).
If you have any questions about Homarr or want to share information with us, please go to one of the following places:
- [Github Discussions](https://github.com/ajnart/homarr/discussions)
- [Discord Server](https://discord.gg/aCsmEV5RgA)
![Screenshot](https://user-images.githubusercontent.com/71191962/169860380-856634fb-4f41-47cb-ba54-6a9e7b3b9c81.gif)

View file

@ -0,0 +1,13 @@
{
"name": "Home Assistant",
"available": true,
"port": 8123,
"id": "homeassistant",
"categories": ["automation"],
"description": "Open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server.",
"short_desc": "Open source home automation that puts local control and privacy first",
"author": "ArneNaessens",
"source": "https://github.com/home-assistant/core",
"image": "/logos/apps/homeassistant.jpg",
"form_fields": []
}

View file

@ -0,0 +1,13 @@
version: '3'
services:
homeassistant:
container_name: homeassistant
image: "ghcr.io/home-assistant/home-assistant:stable"
volumes:
- ${APP_DATA_DIR}/config:/config
restart: unless-stopped
privileged: true
ports:
- ${APP_PORT}:8123
network_mode: host

View file

@ -0,0 +1,7 @@
## Open source home automation that puts local control and privacy first
Open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server.
Check out [home-assistant.io](https://home-assistant.io) for a [demo](https://home-assistant.io/demo/), installation [instructions](https://home-assistant.io/getting-started/), [tutorials](https://home-assistant.io/getting-started/automation/) and [documentation](https://home-assistant.io/docs/)
![Screenshot](https://raw.githubusercontent.com/home-assistant/core/master/docs/screenshots.png)

View file

@ -0,0 +1,13 @@
{
"name": "Invidious",
"available": true,
"port": 8095,
"id": "invidious",
"categories": ["media", "social"],
"description": "Invidious is an open source alternative front-end to YouTube.",
"short_desc": "An alternative front-end to YouTube",
"author": "iv-org",
"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,12 @@
#!/bin/bash
set -eou pipefail
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/channels.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/videos.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/channel_videos.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/users.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/session_ids.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/nonces.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/annotations.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/playlists.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <config/sql/playlist_videos.sql

View file

@ -0,0 +1,12 @@
-- Table: public.annotations
-- DROP TABLE public.annotations;
CREATE TABLE IF NOT EXISTS public.annotations
(
id text NOT NULL,
annotations xml,
CONSTRAINT annotations_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.annotations TO current_user;

View file

@ -0,0 +1,30 @@
-- Table: public.channel_videos
-- DROP TABLE public.channel_videos;
CREATE TABLE IF NOT EXISTS public.channel_videos
(
id text NOT NULL,
title text,
published timestamp with time zone,
updated timestamp with time zone,
ucid text,
author text,
length_seconds integer,
live_now boolean,
premiere_timestamp timestamp with time zone,
views bigint,
CONSTRAINT channel_videos_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.channel_videos TO current_user;
-- Index: public.channel_videos_ucid_idx
-- DROP INDEX public.channel_videos_ucid_idx;
CREATE INDEX IF NOT EXISTS channel_videos_ucid_idx
ON public.channel_videos
USING btree
(ucid COLLATE pg_catalog."default");

View file

@ -0,0 +1,25 @@
-- Table: public.channels
-- DROP TABLE public.channels;
CREATE TABLE IF NOT EXISTS public.channels
(
id text NOT NULL,
author text,
updated timestamp with time zone,
deleted boolean,
subscribed timestamp with time zone,
CONSTRAINT channels_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.channels TO current_user;
-- Index: public.channels_id_idx
-- DROP INDEX public.channels_id_idx;
CREATE INDEX IF NOT EXISTS channels_id_idx
ON public.channels
USING btree
(id COLLATE pg_catalog."default");

View file

@ -0,0 +1,22 @@
-- Table: public.nonces
-- DROP TABLE public.nonces;
CREATE TABLE IF NOT EXISTS public.nonces
(
nonce text,
expire timestamp with time zone,
CONSTRAINT nonces_id_key UNIQUE (nonce)
);
GRANT ALL ON TABLE public.nonces TO current_user;
-- Index: public.nonces_nonce_idx
-- DROP INDEX public.nonces_nonce_idx;
CREATE INDEX IF NOT EXISTS nonces_nonce_idx
ON public.nonces
USING btree
(nonce COLLATE pg_catalog."default");

View file

@ -0,0 +1,19 @@
-- Table: public.playlist_videos
-- DROP TABLE public.playlist_videos;
CREATE TABLE IF NOT EXISTS public.playlist_videos
(
title text,
id text,
author text,
ucid text,
length_seconds integer,
published timestamptz,
plid text references playlists(id),
index int8,
live_now boolean,
PRIMARY KEY (index,plid)
);
GRANT ALL ON TABLE public.playlist_videos TO current_user;

View file

@ -0,0 +1,29 @@
-- Type: public.privacy
-- DROP TYPE public.privacy;
CREATE TYPE public.privacy AS ENUM
(
'Public',
'Unlisted',
'Private'
);
-- Table: public.playlists
-- DROP TABLE public.playlists;
CREATE TABLE IF NOT EXISTS public.playlists
(
title text,
id text primary key,
author text,
description text,
video_count integer,
created timestamptz,
updated timestamptz,
privacy privacy,
index int8[]
);
GRANT ALL ON public.playlists TO current_user;

View file

@ -0,0 +1,23 @@
-- Table: public.session_ids
-- DROP TABLE public.session_ids;
CREATE TABLE IF NOT EXISTS public.session_ids
(
id text NOT NULL,
email text,
issued timestamp with time zone,
CONSTRAINT session_ids_pkey PRIMARY KEY (id)
);
GRANT ALL ON TABLE public.session_ids TO current_user;
-- Index: public.session_ids_id_idx
-- DROP INDEX public.session_ids_id_idx;
CREATE INDEX IF NOT EXISTS session_ids_id_idx
ON public.session_ids
USING btree
(id COLLATE pg_catalog."default");

View file

@ -0,0 +1,29 @@
-- Table: public.users
-- DROP TABLE public.users;
CREATE TABLE IF NOT EXISTS public.users
(
updated timestamp with time zone,
notifications text[],
subscriptions text[],
email text NOT NULL,
preferences text,
password text,
token text,
watched text[],
feed_needs_update boolean,
CONSTRAINT users_email_key UNIQUE (email)
);
GRANT ALL ON TABLE public.users TO current_user;
-- Index: public.email_unique_idx
-- DROP INDEX public.email_unique_idx;
CREATE UNIQUE INDEX IF NOT EXISTS email_unique_idx
ON public.users
USING btree
(lower(email) COLLATE pg_catalog."default");

View file

@ -0,0 +1,23 @@
-- Table: public.videos
-- DROP TABLE public.videos;
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
(
id text NOT NULL,
info text,
updated timestamp with time zone,
CONSTRAINT videos_pkey PRIMARY KEY (id)
);
GRANT ALL ON TABLE public.videos TO current_user;
-- Index: public.id_idx
-- DROP INDEX public.id_idx;
CREATE UNIQUE INDEX IF NOT EXISTS id_idx
ON public.videos
USING btree
(id COLLATE pg_catalog."default");

View file

View file

View file

@ -0,0 +1,47 @@
version: "3.7"
services:
invidious:
user: 1000:1000
container_name: invidious
image: quay.io/invidious/invidious:latest-arm64
restart: unless-stopped
dns:
- ${DNS_IP}
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
networks:
- tipi_main_network
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/init/sql:/config/sql
- ${APP_DATA_DIR}/data/init/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"]
networks:
- tipi_main_network

View file

@ -0,0 +1,48 @@
version: "3.7"
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
networks:
- tipi_main_network
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/init/sql:/config/sql
- ${APP_DATA_DIR}/data/init/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"]
networks:
- tipi_main_network

View file

@ -0,0 +1,27 @@
## An open source alternative front-end to YouTube
**User features**
- Lightweight
- No ads
- No tracking
- No JavaScript required
- Light/Dark themes
- Customizable homepage
- Subscriptions independent from Google
- Notifications for all subscribed channels
- Audio-only mode (with background play on mobile)
- Support for Reddit comments
- Available in many languages, thanks to our translators
<br />
**Data import/export**
- Import subscriptions from YouTube, NewPipe and Freetube
- Import watch history from NewPipe
- Export subscriptions to NewPipe and Freetube
- Import/Export Invidious user data
<br />
**Technical features**
- Embedded video support
- [Developer API](https://docs.invidious.io/api/)
- Does not use official YouTube APIs
- No Contributor License Agreement (CLA)

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

@ -0,0 +1,13 @@
{
"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 ",
"categories": ["media", "utilities"],
"author": "",
"source": "https://github.com/Jackett/Jackett",
"image": "/logos/apps/jackett.jpg",
"form_fields": []
}

View file

@ -0,0 +1,20 @@
version: "3.7"
services:
jackett:
image: lscr.io/linuxserver/jackett:0.20.1342
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:/media/torrents
ports:
- ${APP_PORT}:9117
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,5 @@
## API Support for your favorite torrent trackers
Jackett works as a proxy server: it translates queries from apps ([Sonarr](https://github.com/Sonarr/Sonarr), [Radarr](https://github.com/Radarr/Radarr), [SickRage](https://sickrage.github.io/), [CouchPotato](https://couchpota.to/), [Mylar3](https://github.com/mylar3/mylar3), [Lidarr](https://github.com/lidarr/lidarr), [DuckieTV](https://github.com/SchizoDuckie/DuckieTV), [qBittorrent](https://www.qbittorrent.org/), [Nefarious](https://github.com/lardbit/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. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.
![Screenshot](https://raw.githubusercontent.com/Jackett/Jackett/master/.github/jackett-screenshot1.png)

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

@ -0,0 +1,13 @@
{
"name": "Jellyfin",
"available": true,
"port": 8091,
"id": "jellyfin",
"categories": ["media"],
"description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest!",
"short_desc": "A media server for your home collection",
"author": "jellyfin.org",
"source": "https://github.com/jellyfin/jellyfin",
"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:10.8.1
container_name: jellyfin
volumes:
- ${APP_DATA_DIR}/data/config:/config
- ${ROOT_FOLDER_HOST}/media/data:/media/data
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ}
restart: "unless-stopped"
ports:
- ${APP_PORT}:8096
networks:
- tipi_main_network

View file

@ -0,0 +1,5 @@
## A media server for your home collection
Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET Core framework to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team who want to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest!
For further details, please see [our documentation page](https://docs.jellyfin.org/). To receive the latest updates, get help with Jellyfin, and join the community, please visit [one of our communication channels](https://docs.jellyfin.org/general/getting-help.html). For more information about the project, please see our [about page](https://docs.jellyfin.org/general/about.html).

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

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

@ -0,0 +1,14 @@
{
"name": "Joplin Server",
"available": true,
"port": 8099,
"id": "joplin",
"categories": ["utilities"],
"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": "/logos/apps/joplin.jpg",
"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

View file

@ -0,0 +1,17 @@
## Note taking and to-do application with synchronisation
- **username**: admin@localhost
- **password**: admin
<br />
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.
![Screenshot](https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/home-top-img.png)

View file

@ -0,0 +1,13 @@
{
"name": "LibReddit",
"available": true,
"port": 8105,
"id": "libreddit",
"categories": ["social"],
"description": "LibReddit is a bloat free reddit frontend written in Rust, no ads, no tracking and strong Content Security Policy prevents any request from going to reddit, everything is proxied.",
"short_desc": "Browse reddit without problems!",
"author": "spikecodes",
"source": "https://github.com/spikecodes/libreddit",
"image": "/logos/apps/libreddit.jpg",
"form_fields": []
}

View file

@ -0,0 +1,12 @@
version: "3.7"
services:
libreddit:
container_name: libreddit
image: spikecodes/libreddit:arm
dns:
- ${DNS_IP}
ports:
- ${APP_PORT}:8080
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,12 @@
version: "3.7"
services:
libreddit:
container_name: libreddit
image: spikecodes/libreddit
dns:
- ${DNS_IP}
ports:
- ${APP_PORT}:8080
restart: unless-stopped
networks:
- tipi_main_network

View file

@ -0,0 +1,12 @@
## An alternative private front-end to Reddit
**10 second pitch:** Libreddit is a portmanteau of "libre" (meaning freedom) and "Reddit". It is a private front-end like [Invidious](https://github.com/iv-org/invidious) but for Reddit. Browse the coldest takes of [r/unpopularopinion](https://libreddit.spike.codes/r/unpopularopinion) without being [tracked](#reddit).
- 🚀 Fast: written in Rust for blazing-fast speeds and memory safety
- ☁️ Light: no JavaScript, no ads, no tracking, no bloat
- 🕵 Private: all requests are proxied through the server, including media
- 🔒 Secure: strong [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) prevents browser requests to Reddit
<br />
![Screenshot](https://camo.githubusercontent.com/0753860e104fd86d32e13d47b31c8db88ec86bbdc66911480d92d20a96332c51/68747470733a2f2f692e6962622e636f2f515962715451742f6c69627265646469742d727573742e706e67)

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

@ -0,0 +1,13 @@
{
"name": "Mealie",
"port": 8114,
"available": true,
"id": "mealie",
"description": "Mealie is a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications. Default username / password is changeme@email.com / MyPassword",
"short_desc": "Mealie is a self-hosted recipe manager and meal planner.",
"author": "hay-kot",
"categories": [],
"source": "https://github.com/hay-kot/mealie",
"image": "https://raw.githubusercontent.com/hay-kot/mealie/mealie-next/frontend/static/icons/android-chrome-512x512.png",
"form_fields": []
}

View file

@ -0,0 +1,15 @@
version: "3.7"
services:
mealie:
container_name: mealie
image: hkotel/mealie:v0.5.6
restart: unless-stopped
ports:
- ${APP_PORT}:80
environment:
PUID: 1000
PGID: 1000
volumes:
- ${APP_DATA_DIR}/data:/app/data
networks:
- tipi_main_network

View file

@ -0,0 +1,9 @@
## Mealie is a self-hosted recipe manager and meal planner.
- **username**: changeme@email.com
- **password**: MyPassword
<br />
Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.
[![Product Name Screen Shot](https://raw.githubusercontent.com/hay-kot/mealie/mealie-next/docs/docs/assets/img/home_screenshot.png)](https://docs.mealie.io/)

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

@ -0,0 +1,14 @@
{
"name": "n8n",
"available": true,
"port": 8094,
"id": "n8n",
"categories": ["automation"],
"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": "/logos/apps/n8n.jpg",
"form_fields": []
}

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