commit
bff6c57daf
36 changed files with 369 additions and 30 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,10 +1,9 @@
|
|||
.pnpm-debug.log
|
||||
.env*
|
||||
data/
|
||||
github.secrets
|
||||
node_modules/
|
||||
app-data/*
|
||||
data/
|
||||
data/postgres
|
||||
traefik/ssl/*
|
||||
!traefik/ssl/.gitkeep
|
||||
!app-data/.gitkeep
|
||||
|
@ -16,10 +15,13 @@ state/*
|
|||
|
||||
media/data/movies/*
|
||||
media/data/tv/*
|
||||
media/data/books/*
|
||||
media/data/books/spoken/*
|
||||
media/data/books/ebooks/*
|
||||
!media/data/movies/.gitkeep
|
||||
!media/data/tv/.gitkeep
|
||||
!media/data/books/metadata.db
|
||||
!media/data/books/ebooks/.gitkeep
|
||||
!media/data/books/spoken/.gitkeep
|
||||
|
||||
media/torrents/complete/*
|
||||
!media/torrents/complete/.gitkeep
|
||||
|
|
|
@ -3,7 +3,7 @@ FROM alpine:3.16.0 as app
|
|||
WORKDIR /
|
||||
|
||||
# Install docker
|
||||
RUN apk --no-cache add docker-compose nodejs npm bash
|
||||
RUN apk --no-cache add docker-compose nodejs npm bash g++ make
|
||||
|
||||
RUN npm install node-gyp -g
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Check our demo instance : **95.179.210.152** / username: **user@runtipi.com** /
|
|||
- [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
|
||||
- [Firefly III](https://github.com/firefly-iii/firefly-iii) - A personal finances manager
|
||||
- [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
|
||||
|
@ -38,11 +39,14 @@ Check our demo instance : **95.179.210.152** / username: **user@runtipi.com** /
|
|||
- [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
|
||||
- [Overseerr](https://github.com/sct/overseerr) - Request management and media discovery tool for the Plex ecosystem
|
||||
- [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
|
||||
- [Portainer](https://github.com/portainer/portainer) - Making Docker and Kubernetes management easy.
|
||||
- [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
|
||||
- [Readarr](https://github.com/Readarr/Readarr) - Book Manager and Automation (Sonarr for Ebooks)
|
||||
- [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
|
||||
|
|
35
apps/firefly-iii/config.json
Normal file
35
apps/firefly-iii/config.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "Firefly III",
|
||||
"available": true,
|
||||
"port": 8115,
|
||||
"id": "firefly-iii",
|
||||
"categories": ["finance"],
|
||||
"description": "",
|
||||
"short_desc": "Firefly III: a personal finances manager ",
|
||||
"author": "JC5",
|
||||
"website": "https://www.firefly-iii.org/",
|
||||
"source": "https://github.com/firefly-iii/firefly-iii",
|
||||
"image": "/logos/apps/firefly-iii.jpg",
|
||||
"form_fields": [
|
||||
{
|
||||
"type": "email",
|
||||
"label": "Your email",
|
||||
"required": true,
|
||||
"env_variable": "EMAIL"
|
||||
},
|
||||
{
|
||||
"type": "random",
|
||||
"min": 32,
|
||||
"max": 32,
|
||||
"label": "Random key",
|
||||
"env_variable": "APP_KEY"
|
||||
},
|
||||
{
|
||||
"type": "random",
|
||||
"min": 32,
|
||||
"max": 32,
|
||||
"label": "Database password",
|
||||
"env_variable": "MYSQL_PASSWORD"
|
||||
}
|
||||
]
|
||||
}
|
59
apps/firefly-iii/docker-compose.yml
Normal file
59
apps/firefly-iii/docker-compose.yml
Normal file
|
@ -0,0 +1,59 @@
|
|||
version: '3.9'
|
||||
|
||||
services:
|
||||
firefly-iii:
|
||||
image: fireflyiii/core:latest
|
||||
container_name: firefly-iii
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${APP_DATA_DIR}/data/uplodad:/var/www/html/storage/upload
|
||||
ports:
|
||||
- ${APP_PORT}:8080
|
||||
depends_on:
|
||||
- firefly-iii-db
|
||||
environment:
|
||||
- APP_ENV=local
|
||||
- APP_DEBUG=false
|
||||
- SITE_OWNER=${EMAIL}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- TZ=${TZ}
|
||||
- TRUSTED_PROXIES=**
|
||||
|
||||
# Database
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=firefly-iii-db
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=firefly
|
||||
- DB_USERNAME=firefly
|
||||
- DB_PASSWORD=${MYSQL_PASSWORD}
|
||||
|
||||
# Cookie settings
|
||||
- COOKIE_PATH="/"
|
||||
- COOKIE_DOMAIN=
|
||||
- COOKIE_SECURE=false
|
||||
- COOKIE_SAMESITE=lax
|
||||
|
||||
- APP_NAME=FireflyIII
|
||||
- BROADCAST_DRIVER=log
|
||||
- QUEUE_DRIVER=sync
|
||||
- CACHE_PREFIX=firefly
|
||||
- IS_HEROKU=false
|
||||
- FIREFLY_III_LAYOUT=v1
|
||||
- APP_URL=http://localhost:${APP_PORT}
|
||||
networks:
|
||||
- tipi_main_network
|
||||
|
||||
firefly-iii-db:
|
||||
container_name: firefly-iii-db
|
||||
image: mariadb
|
||||
hostname: fireflyiii-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MYSQL_RANDOM_ROOT_PASSWORD=yes
|
||||
- MYSQL_USER=firefly
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
|
||||
- MYSQL_DATABASE=firefly
|
||||
volumes:
|
||||
- ${APP_DATA_DIR}/data/db:/var/lib/mysql
|
||||
networks:
|
||||
- tipi_main_network
|
26
apps/firefly-iii/metadata/description.md
Normal file
26
apps/firefly-iii/metadata/description.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
"Firefly III" is a (self-hosted) manager for your personal finances. It can help you keep track of your expenses and income, so you can spend less and save more. Firefly III supports the use of budgets, categories and tags. Using a bunch of external tools, you can import data. It also has many neat financial reports available.
|
||||
|
||||
Firefly III should give you **insight** into and **control** over your finances. Money should be useful, not scary. You should be able to *see* where it is going, to *feel* your expenses and to... wow, I'm going overboard with this aren't I?
|
||||
|
||||
But you get the idea: this is your money. These are your expenses. Stop them from controlling you. I built this tool because I started to dislike money. Having money, not having money, paying bills with money, you get the idea. But no more. I want to feel "safe", whatever my balance is. And I hope this tool can help you. I know it helps me.
|
||||
|
||||
![Firefly III on iMac](https://raw.githubusercontent.com/firefly-iii/firefly-iii/develop/.github/assets/img/imac-complete.png)
|
||||
|
||||
### Purpose
|
||||
|
||||
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase their current cashflow. There are tons of ways to save and earn money. Firefly III works on the principle that if you know where your money is going, you can stop it from going there.
|
||||
|
||||
By keeping track of your expenses and your income you can budget accordingly and save money. Stop living from paycheck to paycheck but give yourself the financial wiggle room you need.
|
||||
|
||||
You can read more about the purpose of Firefly III in the [documentation](https://docs.firefly-iii.org/).
|
||||
|
||||
![Firefly III on iPad](https://raw.githubusercontent.com/firefly-iii/firefly-iii/develop/.github/assets/img/ipad-complete.png)
|
||||
|
||||
## Need help?
|
||||
|
||||
If you need support using Firefly III or the associated tools, come find us!
|
||||
|
||||
- [GitHub Discussions for questions and support](https://github.com/firefly-iii/firefly-iii/discussions/)
|
||||
- [Gitter.im for a good chat and a quick answer](https://gitter.im/firefly-iii/firefly-iii)
|
||||
- [GitHub Issues for bugs and issues](https://github.com/firefly-iii/firefly-iii/issues)
|
||||
- [Follow me around for news and updates on Twitter](https://twitter.com/Firefly_iii)
|
13
apps/overseerr/config.json
Normal file
13
apps/overseerr/config.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Overseerr",
|
||||
"available": true,
|
||||
"port": 8116,
|
||||
"id": "overseerr",
|
||||
"categories": ["media", "utilities"],
|
||||
"description": "Overseerr is a free and open source software application for managing requests for your media library. It integrates with your existing services, such as Sonarr, Radarr, and Plex!",
|
||||
"short_desc": "Request management and media discovery tool for the Plex ecosystem",
|
||||
"author": "sct",
|
||||
"source": "https://github.com/sct/overseerr",
|
||||
"image": "/logos/apps/overseerr.jpg",
|
||||
"form_fields": []
|
||||
}
|
16
apps/overseerr/docker-compose.yml
Normal file
16
apps/overseerr/docker-compose.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
version: "3"
|
||||
services:
|
||||
overseerr:
|
||||
container_name: overseerr
|
||||
image: sctx/overseerr:latest
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
volumes:
|
||||
- ${APP_DATA_DIR}/data/config:/app/config
|
||||
ports:
|
||||
- ${APP_PORT}:5055
|
||||
restart: unless-stopped
|
||||
dns:
|
||||
- ${DNS_IP}
|
||||
networks:
|
||||
- tipi_main_network
|
14
apps/overseerr/metadata/description.md
Normal file
14
apps/overseerr/metadata/description.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
**Overseerr** is a free and open source software application for managing requests for your media library. It integrates with your existing services, such as **[Sonarr](https://sonarr.tv/)**, **[Radarr](https://radarr.video/)**, and **[Plex](https://www.plex.tv/)**!
|
||||
|
||||
## Current Features
|
||||
|
||||
- Full Plex integration. Authenticate and manage user access with Plex!
|
||||
- Easy integration with your existing services. Currently, Overseerr supports Sonarr and Radarr. More to come!
|
||||
- Plex library scan, to keep track of the titles which are already available.
|
||||
- Customizable request system, which allows users to request individual seasons or movies in a friendly, easy-to-use interface.
|
||||
- Incredibly simple request management UI. Don't dig through the app to simply approve recent requests!
|
||||
- Granular permission system.
|
||||
- Support for various notification agents.
|
||||
- Mobile-friendly design, for when you need to approve requests on the go!
|
||||
|
||||
With more features on the way! Check out our [issue tracker](https://github.com/sct/overseerr/issues) to see the features which have already been requested.
|
13
apps/portainer/config.json
Normal file
13
apps/portainer/config.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Portainer",
|
||||
"port": 9443,
|
||||
"available": true,
|
||||
"id": "portainer",
|
||||
"categories": ["utilities"],
|
||||
"description": "",
|
||||
"short_desc": "Making Docker and Kubernetes management easy.",
|
||||
"author": "portainer.io",
|
||||
"source": "https://github.com/portainer/portainer",
|
||||
"image": "/logos/apps/portainer.jpg",
|
||||
"form_fields": []
|
||||
}
|
14
apps/portainer/docker-compose.yml
Normal file
14
apps/portainer/docker-compose.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
portainer:
|
||||
image: portainer/portainer-ce:latest
|
||||
container_name: portainer
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${APP_PORT}:9443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- "${APP_DATA_DIR}/data:/data"
|
||||
networks:
|
||||
- tipi_main_network
|
6
apps/portainer/metadata/description.md
Normal file
6
apps/portainer/metadata/description.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
![Screenshot](https://github.com/portainer/portainer/blob/develop/app/assets/images/portainer-github-banner.png?raw=true)
|
||||
|
||||
**Portainer Community Edition** is a lightweight service delivery platform for containerized applications that can be used to manage Docker, Swarm, Kubernetes and ACI environments. It is designed to be as simple to deploy as it is to use. The application allows you to manage all your orchestrator resources (containers, images, volumes, networks and more) through a ‘smart’ GUI and/or an extensive API.
|
||||
|
||||
Portainer consists of a single container that can run on any cluster. It can be deployed as a Linux container or a Windows native container.
|
13
apps/readarr/config.json
Normal file
13
apps/readarr/config.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Readarr",
|
||||
"available": true,
|
||||
"port": 8112,
|
||||
"id": "readarr",
|
||||
"categories": ["books", "media"],
|
||||
"description": "",
|
||||
"short_desc": "Book Manager and Automation (Sonarr for Ebooks)",
|
||||
"author": "readarr.com",
|
||||
"source": "https://github.com/Readarr/Readarr",
|
||||
"image": "/logos/apps/readarr.jpg",
|
||||
"form_fields": []
|
||||
}
|
20
apps/readarr/docker-compose.yml
Normal file
20
apps/readarr/docker-compose.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
readarr:
|
||||
image: lscr.io/linuxserver/readarr:develop
|
||||
container_name: readarr
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=${TZ}
|
||||
dns:
|
||||
- ${DNS_IP}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${APP_DATA_DIR}/data:/config
|
||||
- ${ROOT_FOLDER_HOST}/media:/media
|
||||
ports:
|
||||
- ${APP_PORT}:8787
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- tipi_main_network
|
22
apps/readarr/metadata/description.md
Normal file
22
apps/readarr/metadata/description.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
Readarr is an ebook and audiobook collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new books from your favorite authors and will grab, sort, and rename them.
|
||||
Note that only one type of a given book is supported. If you want both an audiobook and ebook of a given book you will need multiple instances.
|
||||
|
||||
## Major Features Include
|
||||
|
||||
* Can watch for better quality of the ebooks and audiobooks you have and do an automatic upgrade. *e.g. from PDF to AZW3*
|
||||
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.
|
||||
* Automatically detects new books
|
||||
* Can scan your existing library and download any missing books
|
||||
* Automatic failed download handling will try another release if one fails
|
||||
* Manual search so you can pick any release or to see why a release was not downloaded automatically
|
||||
* Advanced customization for profiles, such that Readarr will always download the copy you want
|
||||
* Fully configurable book renaming
|
||||
* SABnzbd, NZBGet, QBittorrent, Deluge, rTorrent, Transmission, uTorrent, and other download clients are supported and integrated
|
||||
* Full integration with Calibre (add to library, conversion) (Requires Calibre Content Server)
|
||||
* And a beautiful UI
|
||||
|
||||
## Support
|
||||
|
||||
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/readarr)
|
||||
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://readarr.com/discord)
|
||||
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/readarr)
|
|
@ -29,7 +29,7 @@ services:
|
|||
POSTGRES_USER: tipi
|
||||
POSTGRES_DB: tipi
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -d tipi -U tipi" ]
|
||||
test: ["CMD-SHELL", "pg_isready -d tipi -U tipi"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 120
|
||||
|
@ -59,6 +59,9 @@ services:
|
|||
POSTGRES_USERNAME: tipi
|
||||
POSTGRES_DBNAME: tipi
|
||||
POSTGRES_HOST: tipi-db
|
||||
NODE_ENV: production
|
||||
dns:
|
||||
- ${DNS_IP}
|
||||
networks:
|
||||
- tipi_main_network
|
||||
|
||||
|
@ -71,7 +74,8 @@ services:
|
|||
networks:
|
||||
- tipi_main_network
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
INTERNAL_IP: ${INTERNAL_IP}
|
||||
NODE_ENV: production
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
|
||||
|
|
|
@ -29,7 +29,7 @@ services:
|
|||
POSTGRES_USER: tipi
|
||||
POSTGRES_DB: tipi
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -d tipi -U tipi" ]
|
||||
test: ["CMD-SHELL", "pg_isready -d tipi -U tipi"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 120
|
||||
|
@ -60,6 +60,9 @@ services:
|
|||
POSTGRES_USERNAME: tipi
|
||||
POSTGRES_DBNAME: tipi
|
||||
POSTGRES_HOST: tipi-db
|
||||
NODE_ENV: production
|
||||
dns:
|
||||
- ${DNS_IP}
|
||||
networks:
|
||||
- tipi_main_network
|
||||
|
||||
|
@ -73,7 +76,8 @@ services:
|
|||
networks:
|
||||
- tipi_main_network
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
INTERNAL_IP: ${INTERNAL_IP}
|
||||
NODE_ENV: production
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
|
||||
|
|
0
media/data/books/ebooks/.gitkeep
Normal file
0
media/data/books/ebooks/.gitkeep
Normal file
0
media/data/books/spoken/.gitkeep
Normal file
0
media/data/books/spoken/.gitkeep
Normal file
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "runtipi",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"description": "A homeserver for everyone",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dashboard",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "jest --colors",
|
||||
|
|
BIN
packages/dashboard/public/logos/apps/firefly-iii.jpg
Normal file
BIN
packages/dashboard/public/logos/apps/firefly-iii.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
packages/dashboard/public/logos/apps/overseerr.jpg
Normal file
BIN
packages/dashboard/public/logos/apps/overseerr.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
packages/dashboard/public/logos/apps/portainer.jpg
Normal file
BIN
packages/dashboard/public/logos/apps/portainer.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
packages/dashboard/public/logos/apps/readarr.jpg
Normal file
BIN
packages/dashboard/public/logos/apps/readarr.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
|
@ -13,4 +13,5 @@ export const APP_CATEGORIES = [
|
|||
{ name: 'Books', id: AppCategoriesEnum.Books, icon: 'FaBook' },
|
||||
{ name: 'Data', id: AppCategoriesEnum.Data, icon: 'FaDatabase' },
|
||||
{ name: 'Music', id: AppCategoriesEnum.Music, icon: 'FaMusic' },
|
||||
{ name: 'Finance', id: AppCategoriesEnum.Finance, icon: 'FaMoneyBillAlt' },
|
||||
];
|
||||
|
|
|
@ -37,6 +37,7 @@ export enum AppCategoriesEnum {
|
|||
Data = 'DATA',
|
||||
Development = 'DEVELOPMENT',
|
||||
Featured = 'FEATURED',
|
||||
Finance = 'FINANCE',
|
||||
Media = 'MEDIA',
|
||||
Music = 'MUSIC',
|
||||
Network = 'NETWORK',
|
||||
|
|
|
@ -38,4 +38,5 @@ export const colorSchemeForCategory: Record<AppCategoriesEnum, string> = {
|
|||
[AppCategoriesEnum.Data]: 'red',
|
||||
[AppCategoriesEnum.Books]: 'blue',
|
||||
[AppCategoriesEnum.Music]: 'green',
|
||||
[AppCategoriesEnum.Finance]: 'orange',
|
||||
};
|
||||
|
|
|
@ -11,6 +11,9 @@ interface IProps {
|
|||
initalValues?: Record<string, string>;
|
||||
}
|
||||
|
||||
const hiddenTypes = ['random'];
|
||||
const typeFilter = (field: FormField) => !hiddenTypes.includes(field.type);
|
||||
|
||||
const InstallForm: React.FC<IProps> = ({ formFields, onSubmit, initalValues }) => {
|
||||
const renderField = (field: FormField) => {
|
||||
return (
|
||||
|
@ -30,7 +33,7 @@ const InstallForm: React.FC<IProps> = ({ formFields, onSubmit, initalValues }) =
|
|||
validate={(values) => validateAppConfig(values, formFields)}
|
||||
render={({ handleSubmit, validating, submitting }) => (
|
||||
<form className="flex flex-col" onSubmit={handleSubmit}>
|
||||
{formFields.map(renderField)}
|
||||
{formFields.filter(typeFilter).map(renderField)}
|
||||
<Button isLoading={validating || submitting} className="self-end mb-2" colorScheme="green" type="submit">
|
||||
{initalValues ? 'Update' : 'Install'}
|
||||
</Button>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "system-api",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"description": "",
|
||||
"exports": "./dist/server.js",
|
||||
"type": "module",
|
||||
|
|
|
@ -20,6 +20,8 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
dotenv.config({ path: '.env' });
|
||||
}
|
||||
|
||||
console.log('Production => ', process.env.NODE_ENV === 'production');
|
||||
|
||||
const {
|
||||
LOGS_FOLDER = 'logs',
|
||||
LOGS_APP = 'app.log',
|
||||
|
|
38
packages/system-api/src/core/updates/recover-migrations.ts
Normal file
38
packages/system-api/src/core/updates/recover-migrations.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import datasource from '../../config/datasource';
|
||||
import App from '../../modules/apps/app.entity';
|
||||
import User from '../../modules/auth/user.entity';
|
||||
import Update from '../../modules/system/update.entity';
|
||||
|
||||
const recover = async () => {
|
||||
console.log('Recovering broken database');
|
||||
const apps = await App.find();
|
||||
const users = await User.find();
|
||||
const updated = await Update.find();
|
||||
|
||||
// drop database
|
||||
await datasource.dropDatabase();
|
||||
|
||||
console.log('running migrations');
|
||||
await datasource.runMigrations();
|
||||
|
||||
// create users
|
||||
for (const user of users) {
|
||||
await User.create(user).save();
|
||||
}
|
||||
|
||||
// create apps
|
||||
for (const app of apps) {
|
||||
await App.create(app).save();
|
||||
}
|
||||
|
||||
// create updates
|
||||
for (const update of updated) {
|
||||
await Update.create(update).save();
|
||||
}
|
||||
|
||||
console.log('Users recovered', users.length);
|
||||
console.log('Apps recovered', apps.length);
|
||||
console.log('Database recovered');
|
||||
};
|
||||
|
||||
export default recover;
|
|
@ -1,6 +1,7 @@
|
|||
import portUsed from 'tcp-port-used';
|
||||
import { fileExists, readdirSync, readFile, readJsonFile, runScript, writeFile } from '../fs/fs.helpers';
|
||||
import InternalIp from 'internal-ip';
|
||||
import crypto from 'crypto';
|
||||
import config from '../../config';
|
||||
import { AppInfo } from './apps.types';
|
||||
|
||||
|
@ -84,17 +85,33 @@ export const ensureAppState = (appName: string, installed: boolean) => {
|
|||
writeFile('/state/apps.json', JSON.stringify(state));
|
||||
};
|
||||
|
||||
const getEntropy = (name: string, length: number) => {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(name);
|
||||
return hash.digest('hex').substring(0, length);
|
||||
};
|
||||
|
||||
export const generateEnvFile = (appName: string, form: Record<string, string>) => {
|
||||
const configFile: AppInfo = readJsonFile(`/apps/${appName}/config.json`);
|
||||
const baseEnvFile = readFile('/.env').toString();
|
||||
let envFile = `${baseEnvFile}\nAPP_PORT=${configFile.port}\n`;
|
||||
const envMap = getEnvMap(appName);
|
||||
|
||||
configFile.form_fields?.forEach((field) => {
|
||||
const formValue = form[field.env_variable];
|
||||
const envVar = field.env_variable;
|
||||
|
||||
if (formValue) {
|
||||
const envVar = field.env_variable;
|
||||
envFile += `${envVar}=${formValue}\n`;
|
||||
} else if (field.type === 'random') {
|
||||
if (envMap.has(envVar)) {
|
||||
envFile += `${envVar}=${envMap.get(envVar)}\n`;
|
||||
} else {
|
||||
const length = field.min || 32;
|
||||
const randomString = getEntropy(field.env_variable, length);
|
||||
|
||||
envFile += `${envVar}=${randomString}\n`;
|
||||
}
|
||||
} else if (field.required) {
|
||||
throw new Error(`Variable ${field.env_variable} is required`);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ export enum AppCategoriesEnum {
|
|||
BOOKS = 'books',
|
||||
DATA = 'data',
|
||||
MUSIC = 'music',
|
||||
FINANCE = 'finance',
|
||||
}
|
||||
|
||||
export enum FieldTypes {
|
||||
|
@ -25,6 +26,7 @@ export enum FieldTypes {
|
|||
ip = 'ip',
|
||||
fqdnip = 'fqdnip',
|
||||
url = 'url',
|
||||
random = 'random',
|
||||
}
|
||||
|
||||
export enum AppStatusEnum {
|
||||
|
|
|
@ -14,21 +14,24 @@ import cors from 'cors';
|
|||
import datasource from './config/datasource';
|
||||
import appsService from './modules/apps/apps.service';
|
||||
import { runUpdates } from './core/updates/run';
|
||||
import recover from './core/updates/recover-migrations';
|
||||
|
||||
const corsOptions = {
|
||||
credentials: true,
|
||||
origin: function (origin: any, callback: any) {
|
||||
// disallow requests with no origin
|
||||
if (!origin) return callback(new Error('Not allowed by CORS'), false);
|
||||
let corsOptions = __prod__
|
||||
? {
|
||||
credentials: true,
|
||||
origin: function (origin: any, callback: any) {
|
||||
// disallow requests with no origin
|
||||
if (!origin) return callback(new Error('Not allowed by CORS'), false);
|
||||
|
||||
if (config.CLIENT_URLS.includes(origin)) {
|
||||
return callback(null, true);
|
||||
if (config.CLIENT_URLS.includes(origin)) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
const message = "The CORS policy for this origin doesn't allow access from the particular origin.";
|
||||
return callback(new Error(message), false);
|
||||
},
|
||||
}
|
||||
|
||||
const message = "The CORS policy for this origin doesn't allow access from the particular origin.";
|
||||
return callback(new Error(message), false);
|
||||
},
|
||||
};
|
||||
: {};
|
||||
|
||||
const main = async () => {
|
||||
try {
|
||||
|
@ -40,10 +43,6 @@ const main = async () => {
|
|||
|
||||
await datasource.initialize();
|
||||
|
||||
if (__prod__) {
|
||||
await datasource.runMigrations();
|
||||
}
|
||||
|
||||
const schema = await createSchema();
|
||||
const httpServer = createServer(app);
|
||||
const plugins = [ApolloLogs];
|
||||
|
@ -61,13 +60,22 @@ const main = async () => {
|
|||
await apolloServer.start();
|
||||
apolloServer.applyMiddleware({ app, cors: corsOptions });
|
||||
|
||||
if (__prod__) {
|
||||
try {
|
||||
await datasource.runMigrations();
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
await recover();
|
||||
}
|
||||
}
|
||||
|
||||
// Run migrations
|
||||
await runUpdates();
|
||||
|
||||
httpServer.listen(port, () => {
|
||||
// Start apps
|
||||
appsService.startAllApps();
|
||||
logger.info(`Server running on port ${port}`);
|
||||
console.info(`Server running on port ${port} 🚀 Production => ${__prod__}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
1
scripts/unsafe-cleanup.sh
Normal file → Executable file
1
scripts/unsafe-cleanup.sh
Normal file → Executable file
|
@ -22,6 +22,7 @@ echo y | docker image prune -a
|
|||
|
||||
# Remove everything in app-data folder
|
||||
rm -rf "${ROOT_FOLDER}/app-data"
|
||||
rm -rf "${ROOT_FOLDER}/data/postgres"
|
||||
mkdir -p "${ROOT_FOLDER}/app-data"
|
||||
|
||||
# Put {"installed":""} in state/apps.json
|
||||
|
|
Loading…
Reference in a new issue