commit
cbeec6eb75
12 changed files with 164 additions and 14 deletions
|
@ -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
|
||||
|
|
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.
|
||||
|
||||

|
||||
|
||||
### 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/).
|
||||
|
||||

|
||||
|
||||
## 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)
|
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 |
|
@ -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,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 {
|
||||
|
|
|
@ -15,20 +15,24 @@ import datasource from './config/datasource';
|
|||
import appsService from './modules/apps/apps.service';
|
||||
import { runUpdates } from './core/updates/run';
|
||||
|
||||
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 = {};
|
||||
|
||||
if (config.CLIENT_URLS.includes(origin)) {
|
||||
return callback(null, true);
|
||||
}
|
||||
if (__prod__) {
|
||||
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);
|
||||
|
||||
const message = "The CORS policy for this origin doesn't allow access from the particular origin.";
|
||||
return callback(new Error(message), false);
|
||||
},
|
||||
};
|
||||
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 main = async () => {
|
||||
try {
|
||||
|
|
Loading…
Add table
Reference in a new issue