Move system-api in a docker comntainer
This commit is contained in:
parent
8d1b53c905
commit
4e03ca01f1
28 changed files with 204 additions and 166 deletions
23
.github/workflows/build-images.yml
vendored
23
.github/workflows/build-images.yml
vendored
|
@ -25,10 +25,29 @@ jobs:
|
|||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
name: Get tag from VERSION file
|
||||
id: meta
|
||||
run: |
|
||||
VERSION=$(cat VERSION)
|
||||
TAG=${VERSION}
|
||||
echo "::set-output name=tag::${TAG}"
|
||||
-
|
||||
name: Build and push dashboard
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./packages/dashboard
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: meienberger/tipi-dashboard:latest
|
||||
tags: meienberger/tipi-dashboard:latest,meienberger/tipi-dashboard:${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-dashboard:latest
|
||||
cache-to: type=inline
|
||||
-
|
||||
name: Build and push api
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./packages/system-api
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: meienberger/tipi-api:latest,meienberger/tipi-api:${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-api:latest
|
||||
cache-to: type=inline
|
33
.github/workflows/verify-release.yml
vendored
Normal file
33
.github/workflows/verify-release.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: Verify release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- uses: technote-space/get-diff-action@v6
|
||||
with:
|
||||
FILES: |
|
||||
VERSION
|
||||
|
||||
- name: Ensure env.MATCHED_FILES has VERSION in it
|
||||
id: check-version
|
||||
run: |
|
||||
if [[ -z "${{ env.MATCHED_FILES }}" ]]; then
|
||||
echo "::error::VERSION not modified"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${{ env.MATCHED_FILES }}" =~ VERSION ]]; then
|
||||
echo "::error::VERSION not modified"
|
||||
exit 1
|
||||
fi
|
||||
|
1
VERSION
Normal file
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.1.2
|
|
@ -1,6 +1,4 @@
|
|||
packages:
|
||||
- jq
|
||||
- ufw
|
||||
- coreutils
|
||||
- git
|
||||
- docker
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
- hosts: tipi
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- import_tasks: ./tasks/common/packages.yml
|
|
@ -3,12 +3,6 @@
|
|||
update_cache: yes
|
||||
upgrade: yes
|
||||
|
||||
- name: Install node 16
|
||||
shell: curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
|
||||
|
||||
- name: Install node
|
||||
shell: apt-get install -y nodejs
|
||||
|
||||
- name: Install essential packages
|
||||
package:
|
||||
name: "{{ packages }}"
|
||||
|
@ -26,37 +20,9 @@
|
|||
line: "{{ username }} ALL=(ALL) NOPASSWD: ALL"
|
||||
validate: "/usr/sbin/visudo -cf %s"
|
||||
|
||||
- name: Restart SSH daemon
|
||||
service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
|
||||
- name: Allow SSH in UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: 22
|
||||
proto: tcp
|
||||
|
||||
- name: Allow port 80 in UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: 80
|
||||
proto: tcp
|
||||
|
||||
- name: Allow port 443 in UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: 443
|
||||
proto: tcp
|
||||
|
||||
- name: Allow ports for apps
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: 3000:3001
|
||||
proto: tcp
|
||||
|
||||
- name: Enable ufw daemon
|
||||
service:
|
||||
name: ufw
|
||||
state: started
|
||||
enabled: yes
|
||||
- name: Create cron every minute running system-info.sh
|
||||
cron:
|
||||
name: "system-info"
|
||||
user: "{{ username }}"
|
||||
minute: "*/1"
|
||||
command: "{{ playbook_dir }}/../scripts/system-info.sh"
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
- name: Install "pm2" package globally.
|
||||
community.general.npm:
|
||||
name: pm2
|
||||
global: yes
|
||||
|
||||
- name: Install "pnpm" package globally.
|
||||
community.general.npm:
|
||||
name: pnpm
|
||||
global: yes
|
||||
|
||||
- name: Run pm2 first time
|
||||
shell: pm2 list
|
||||
|
||||
- name: Enable pm2 as a service
|
||||
shell: sudo env PATH=$PATH:/usr/local/bin pm2 startup -u {{ username }}
|
||||
|
||||
- name: Install dependencies
|
||||
shell: cd {{ playbook_dir }} && pnpm install
|
||||
|
||||
- name: Clean packages
|
||||
shell: cd {{ playbook_dir }} && pnpm -r clean
|
||||
|
||||
- name: Build packages
|
||||
become_user: "{{ username }}"
|
||||
shell: cd {{ playbook_dir }} && pnpm -r build-prod
|
||||
|
||||
- name: Check if app is already running
|
||||
become_user: "{{ username }}"
|
||||
shell: pm2 status system-api
|
||||
register: pm2_result
|
||||
|
||||
- name: Start app
|
||||
become_user: "{{ username }}"
|
||||
shell: cd {{ playbook_dir }}/../packages/system-api && pm2 start npm --name "system-api" -- start
|
||||
when: pm2_result.stdout.find("online") == -1
|
||||
|
||||
- name: Reload app
|
||||
become_user: "{{ username }}"
|
||||
shell: pm2 reload system-api
|
||||
when: pm2_result.stdout.find("online") != -1
|
|
@ -1,9 +1,15 @@
|
|||
- name: Check if pm2 is installed
|
||||
stat:
|
||||
path: /usr/local/bin/pm2
|
||||
register: pm2_status
|
||||
|
||||
- name: Check if app is already running
|
||||
become_user: "{{ username }}"
|
||||
shell: pm2 list
|
||||
register: pm2_result
|
||||
when: pm2_status.stat.exists
|
||||
|
||||
- name: Stop app
|
||||
become_user: "{{ username }}"
|
||||
shell: pm2 stop "system-api"
|
||||
when: pm2_result.stdout.find("system-api") != -1
|
||||
when: pm2_status.stat.exists && pm2_result.stdout.find("system-api") != -1
|
||||
|
|
|
@ -18,7 +18,7 @@ services:
|
|||
|
||||
|
||||
api:
|
||||
image: meienberger/tipi-api:latest
|
||||
image: meienberger/tipi-api:${TIPI_VERSION}
|
||||
container_name: api
|
||||
ports:
|
||||
- 3001:3001
|
||||
|
@ -28,18 +28,22 @@ services:
|
|||
- ${PWD}:/tipi
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
- TIPI_VERSION=${TIPI_VERSION}
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
networks:
|
||||
tipi_main_network:
|
||||
ipv4_address: 10.21.21.3
|
||||
|
||||
dashboard:
|
||||
image: meienberger/tipi-dashboard:latest
|
||||
image: meienberger/tipi-dashboard:${TIPI_VERSION}
|
||||
container_name: dashboard
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
tipi_main_network:
|
||||
ipv4_address: 10.21.21.4
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
"scripts": {
|
||||
"prepare": "husky install",
|
||||
"act:test-install": "act --container-architecture linux/amd64 -j test-install",
|
||||
"act:docker": "act --container-architecture linux/amd64 --secret-file github.secrets -j docker",
|
||||
"docker:build": "docker build ./packages/system-api/ -t meienberger/tipi-api:latest && docker build ./packages/dashboard/ -t meienberger/tipi-dashboard:latest"
|
||||
"act:docker": "act --container-architecture linux/amd64 --secret-file github.secrets -j docker"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^8.15.0",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
const { NODE_ENV, INTERNAL_IP } = process.env;
|
||||
|
||||
const nextConfig = {
|
||||
reactStrictMode: false,
|
||||
reactStrictMode: true,
|
||||
env: {
|
||||
INTERNAL_IP: NODE_ENV === 'development' ? 'localhost' : INTERNAL_IP,
|
||||
INTERNAL_IP: INTERNAL_IP,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import axios, { Method } from 'axios';
|
||||
|
||||
export const BASE_URL = 'http://localhost:3001';
|
||||
import { useSytemStore } from '../state/systemStore';
|
||||
|
||||
interface IFetchParams {
|
||||
endpoint: string;
|
||||
|
@ -12,6 +11,9 @@ interface IFetchParams {
|
|||
const api = async <T = unknown>(fetchParams: IFetchParams): Promise<T> => {
|
||||
const { endpoint, method = 'GET', params, data } = fetchParams;
|
||||
|
||||
const { getState } = useSytemStore;
|
||||
const BASE_URL = `http://${getState().internalIp}:3001`;
|
||||
|
||||
const response = await axios.request<T & { error?: string }>({
|
||||
method,
|
||||
params,
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { BareFetcher } from 'swr';
|
||||
import axios from 'axios';
|
||||
import { BASE_URL } from './api';
|
||||
import { useSytemStore } from '../state/systemStore';
|
||||
|
||||
const fetcher: BareFetcher<any> = (url: string) => {
|
||||
const { getState } = useSytemStore;
|
||||
const BASE_URL = `http://${getState().internalIp}:3001`;
|
||||
|
||||
return axios.get(url, { baseURL: BASE_URL, withCredentials: true }).then((res) => res.data);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import { FiExternalLink } from 'react-icons/fi';
|
||||
import { AppConfig } from '../../../core/types';
|
||||
import { useAppsStore } from '../../../state/appsStore';
|
||||
import { useSytemStore } from '../../../state/systemStore';
|
||||
import AppActions from '../components/AppActions';
|
||||
import InstallModal from '../components/InstallModal';
|
||||
import StopModal from '../components/StopModal';
|
||||
|
@ -21,6 +22,7 @@ const AppDetails: React.FC<IProps> = ({ app }) => {
|
|||
const updateDisclosure = useDisclosure();
|
||||
|
||||
const { install, update, uninstall, stop, start, fetchApp } = useAppsStore();
|
||||
const { internalIp } = useSytemStore();
|
||||
|
||||
const handleError = (error: unknown) => {
|
||||
if (error instanceof Error) {
|
||||
|
@ -86,7 +88,7 @@ const AppDetails: React.FC<IProps> = ({ app }) => {
|
|||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
window.open(`http://${process.env.INTERNAL_IP}:${app.port}`, '_blank');
|
||||
window.open(`http://${internalIp}:${app.port}`, '_blank');
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import axios from 'axios';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import useSWR, { BareFetcher } from 'swr';
|
||||
import LoadingScreen from '../../../components/LoadingScreen';
|
||||
import { useAuthStore } from '../../../state/authStore';
|
||||
import { useSytemStore } from '../../../state/systemStore';
|
||||
import Login from './Login';
|
||||
import Onboarding from './Onboarding';
|
||||
|
||||
|
@ -8,9 +11,16 @@ interface IProps {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const fetcher: BareFetcher<any> = (url: string) => {
|
||||
return axios.get(url).then((res) => res.data);
|
||||
};
|
||||
|
||||
const AuthWrapper: React.FC<IProps> = ({ children }) => {
|
||||
const [initialLoad, setInitialLoad] = useState(true);
|
||||
const { configured, user, me, fetchConfigured } = useAuthStore();
|
||||
const { internalIp, setInternalIp } = useSytemStore();
|
||||
|
||||
const { data } = useSWR('/api/ip', fetcher);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUser = async () => {
|
||||
|
@ -19,8 +29,14 @@ const AuthWrapper: React.FC<IProps> = ({ children }) => {
|
|||
|
||||
setInitialLoad(false);
|
||||
};
|
||||
if (!user) fetchUser();
|
||||
}, [fetchConfigured, me, user]);
|
||||
if (!user && internalIp) fetchUser();
|
||||
}, [fetchConfigured, internalIp, me, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.ip && !internalIp) {
|
||||
setInternalIp(data.ip);
|
||||
}
|
||||
}, [data?.ip, internalIp, setInternalIp]);
|
||||
|
||||
if (initialLoad && !user) {
|
||||
return <LoadingScreen />;
|
||||
|
|
|
@ -25,12 +25,12 @@ const Dashboard: React.FC = () => {
|
|||
|
||||
// Convert bytes to GB
|
||||
const diskFree = Math.round(disk.available / 1024 / 1024 / 1024);
|
||||
const diskSize = Math.round(disk.size / 1024 / 1024 / 1024);
|
||||
const diskSize = Math.round(disk.total / 1024 / 1024 / 1024);
|
||||
const diskUsed = diskSize - diskFree;
|
||||
const percentUsed = Math.round((diskUsed / diskSize) * 100);
|
||||
|
||||
const memoryTotal = Math.round(memory?.total / 1024 / 1024 / 1024);
|
||||
const memoryFree = Math.round(memory?.free / 1024 / 1024 / 1024);
|
||||
const memoryFree = Math.round(memory?.available / 1024 / 1024 / 1024);
|
||||
const percentUsedMemory = Math.round(((memoryTotal - memoryFree) / memoryTotal) * 100);
|
||||
|
||||
return (
|
||||
|
|
5
packages/dashboard/src/pages/api/ip.tsx
Normal file
5
packages/dashboard/src/pages/api/ip.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default function handler(_: any, res: any) {
|
||||
const { INTERNAL_IP } = process.env;
|
||||
|
||||
res.status(200).json({ ip: INTERNAL_IP });
|
||||
}
|
|
@ -3,26 +3,25 @@ import { Text } from '@chakra-ui/react';
|
|||
import useSWR from 'swr';
|
||||
import Layout from '../components/Layout';
|
||||
import fetcher from '../core/fetcher';
|
||||
import Package from '../../package.json';
|
||||
|
||||
const Settings: NextPage = () => {
|
||||
const { data: latestVersion } = useSWR<string>('/system/version/latest', fetcher);
|
||||
const { data } = useSWR<{ current: string; latest: string }>('/system/version', fetcher);
|
||||
|
||||
const isLatest = latestVersion === `v${Package.version}`;
|
||||
const isLatest = data?.latest === data?.current;
|
||||
|
||||
const renderUpdate = () => {
|
||||
if (isLatest) {
|
||||
return (
|
||||
<Text fontSize="md" color="green.500">
|
||||
Your Tipi install is up to date. Version {Package.version}
|
||||
Your Tipi install is up to date. Version {data?.current}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text fontSize="md">
|
||||
You are not using the latest version of Tipi. There is a new version ({latestVersion}) available. Visit{' '}
|
||||
<a className="text-blue-600" target="_blank" rel="noreferrer" href={`https://github.com/meienberger/runtipi/releases/${latestVersion}`}>
|
||||
You are not using the latest version of Tipi. There is a new version ({data?.latest}) available. Visit{' '}
|
||||
<a className="text-blue-600" target="_blank" rel="noreferrer" href={`https://github.com/meienberger/runtipi/releases/v${data?.latest}`}>
|
||||
Github
|
||||
</a>{' '}
|
||||
for update instructions.
|
||||
|
@ -31,7 +30,7 @@ const Settings: NextPage = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Layout loading={!latestVersion}>
|
||||
<Layout loading={!data}>
|
||||
<Text fontSize="3xl" className="font-bold">
|
||||
Settings
|
||||
</Text>
|
||||
|
|
|
@ -3,17 +3,21 @@ import api from '../core/api';
|
|||
|
||||
type Store = {
|
||||
cpuLoad: number;
|
||||
disk: { size: number; used: number; available: number };
|
||||
memory: { total: number; used: number; free: number };
|
||||
internalIp: string;
|
||||
disk: { total: number; used: number; available: number };
|
||||
memory: { total: number; used: number; available: number };
|
||||
fetchDiskSpace: () => void;
|
||||
fetchCpuLoad: () => void;
|
||||
fetchMemoryLoad: () => void;
|
||||
setInternalIp: (internalIp: string) => void;
|
||||
};
|
||||
|
||||
export const useSytemStore = create<Store>((set) => ({
|
||||
cpuLoad: 0,
|
||||
memory: { total: 0, used: 0, free: 0 },
|
||||
disk: { size: 0, used: 0, available: 0 },
|
||||
internalIp: '',
|
||||
setInternalIp: (internalIp: string) => set((state) => ({ ...state, internalIp })),
|
||||
memory: { total: 0, used: 0, available: 0 },
|
||||
disk: { total: 0, used: 0, available: 0 },
|
||||
fetchDiskSpace: async () => {
|
||||
const response = await api.fetch<any>({
|
||||
endpoint: '/system/disk',
|
||||
|
|
|
@ -5,17 +5,19 @@ interface IConfig {
|
|||
ROOT_FOLDER: string;
|
||||
JWT_SECRET: string;
|
||||
CLIENT_URLS: string[];
|
||||
VERSION: string;
|
||||
}
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const { NODE_ENV = 'development', JWT_SECRET = '' } = process.env;
|
||||
const { NODE_ENV = 'development', JWT_SECRET = '', INTERNAL_IP = '', TIPI_VERSION = '' } = process.env;
|
||||
|
||||
const config: IConfig = {
|
||||
NODE_ENV,
|
||||
ROOT_FOLDER: '/tipi',
|
||||
JWT_SECRET,
|
||||
CLIENT_URLS: ['http://locahost:3000', 'http://10.21.21.4', 'http://10.21.21.4:3000'],
|
||||
CLIENT_URLS: ['http://localhost:3000', `http://${INTERNAL_IP}`, `http://${INTERNAL_IP}:3000`],
|
||||
VERSION: TIPI_VERSION,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
@ -1,34 +1,42 @@
|
|||
import { Request, Response } from 'express';
|
||||
import si from 'systeminformation';
|
||||
import fetch from 'node-fetch';
|
||||
import config from '../../config';
|
||||
import TipiCache from '../../config/cache';
|
||||
import { readJsonFile } from '../fs/fs.helpers';
|
||||
|
||||
type CpuData = {
|
||||
load: number;
|
||||
};
|
||||
|
||||
type DiskData = {
|
||||
size: number;
|
||||
total: number;
|
||||
used: number;
|
||||
available: number;
|
||||
};
|
||||
|
||||
type MemoryData = {
|
||||
total: number;
|
||||
free: number;
|
||||
available: number;
|
||||
used: number;
|
||||
};
|
||||
|
||||
type SystemInfo = {
|
||||
cpu: CpuData;
|
||||
disk: DiskData;
|
||||
memory: MemoryData;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
const getCpuInfo = async (req: Request, res: Response<CpuData>) => {
|
||||
// const cpuInfo = await cpu.getCpuInfo();
|
||||
const cpuLoad = await si.currentLoad();
|
||||
const systemInfo: SystemInfo = readJsonFile('/state/system-info.json');
|
||||
|
||||
res.status(200).send({ load: cpuLoad.currentLoad });
|
||||
const cpu = systemInfo.cpu;
|
||||
|
||||
res.status(200).send({ load: cpu.load });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -37,19 +45,9 @@ const getCpuInfo = async (req: Request, res: Response<CpuData>) => {
|
|||
* @param res
|
||||
*/
|
||||
const getDiskInfo = async (req: Request, res: Response<DiskData>) => {
|
||||
const disk = await si.fsSize();
|
||||
const systemInfo: SystemInfo = readJsonFile('/state/system-info.json');
|
||||
|
||||
const rootDisk = disk.find((item) => item.mount === '/');
|
||||
|
||||
if (!rootDisk) {
|
||||
throw new Error('Could not find root disk');
|
||||
}
|
||||
|
||||
const result: DiskData = {
|
||||
size: rootDisk.size,
|
||||
used: rootDisk.used,
|
||||
available: rootDisk.available,
|
||||
};
|
||||
const result: DiskData = systemInfo.disk;
|
||||
|
||||
res.status(200).send(result);
|
||||
};
|
||||
|
@ -60,32 +58,24 @@ const getDiskInfo = async (req: Request, res: Response<DiskData>) => {
|
|||
* @param res
|
||||
*/
|
||||
const getMemoryInfo = async (req: Request, res: Response<MemoryData>) => {
|
||||
const memory = await si.mem();
|
||||
const systemInfo: SystemInfo = readJsonFile('/state/system-info.json');
|
||||
|
||||
const result: MemoryData = {
|
||||
total: memory.total,
|
||||
free: memory.free,
|
||||
used: memory.used,
|
||||
};
|
||||
const result: MemoryData = systemInfo.memory;
|
||||
|
||||
res.status(200).json(result);
|
||||
};
|
||||
|
||||
const getLatestVersion = async (req: Request, res: Response<string>) => {
|
||||
const getVersion = async (req: Request, res: Response<{ current: string; latest: string }>) => {
|
||||
let version = TipiCache.get<string>('latestVersion');
|
||||
|
||||
console.log('CACHED', version);
|
||||
|
||||
if (!version) {
|
||||
const response = await fetch('https://api.github.com/repos/meienberger/runtipi/releases/latest');
|
||||
const json = (await response.json()) as { name: string };
|
||||
TipiCache.set('latestVersion', json.name);
|
||||
version = json.name;
|
||||
version = json.name.replace('v', '');
|
||||
}
|
||||
|
||||
console.log(version);
|
||||
|
||||
res.status(200).send(version);
|
||||
res.status(200).send({ current: config.VERSION, latest: version });
|
||||
};
|
||||
|
||||
export default { getCpuInfo, getDiskInfo, getMemoryInfo, getLatestVersion };
|
||||
export default { getCpuInfo, getDiskInfo, getMemoryInfo, getVersion };
|
||||
|
|
|
@ -6,6 +6,6 @@ const router = Router();
|
|||
router.route('/cpu').get(SystemController.getCpuInfo);
|
||||
router.route('/disk').get(SystemController.getDiskInfo);
|
||||
router.route('/memory').get(SystemController.getMemoryInfo);
|
||||
router.route('/version/latest').get(SystemController.getLatestVersion);
|
||||
router.route('/version').get(SystemController.getVersion);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -23,7 +23,22 @@ if (isProd) {
|
|||
app.use(helmet());
|
||||
}
|
||||
|
||||
app.use(cors({ credentials: true, origin: config.CLIENT_URLS }));
|
||||
app.use(
|
||||
cors({
|
||||
credentials: true,
|
||||
origin: function (origin, callback) {
|
||||
// allow requests with no origin
|
||||
if (!origin) return callback(null, true);
|
||||
|
||||
if (config.CLIENT_URLS.indexOf(origin) === -1) {
|
||||
var message = "The CORS policy for this origin doesn't allow access from the particular origin.";
|
||||
return callback(new Error(message), false);
|
||||
}
|
||||
|
||||
return callback(null, true);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Get user from token
|
||||
app.use((req, res, next) => {
|
||||
|
|
|
@ -23,8 +23,6 @@ if ! command -v ansible-playbook > /dev/null; then
|
|||
sudo pip3 install ansible
|
||||
fi
|
||||
|
||||
|
||||
|
||||
ansible-playbook ansible/setup.yml -i ansible/hosts -K -e username="$USERNAME"
|
||||
|
||||
# echo "Configuring permissions..."
|
||||
|
|
|
@ -90,20 +90,14 @@ echo "Generating config files..."
|
|||
[[ -f "${ROOT_FOLDER}/packages/system-api/.env" ]] && rm -f "${ROOT_FOLDER}/packages/system-api/.env"
|
||||
|
||||
# Store paths to intermediary config files
|
||||
ENV_FILE="$ROOT_FOLDER/templates/.env"
|
||||
ENV_FILE_SYSTEM_API="$ROOT_FOLDER/templates/.env-api"
|
||||
|
||||
# Remove intermediary config files
|
||||
[[ -f "$ENV_FILE" ]] && rm -f "$ENV_FILE"
|
||||
[[ -f "$ENV_FILE_SYSTEM_API" ]] && rm -f "$ENV_FILE_SYSTEM_API"
|
||||
ENV_FILE=$(mktemp)
|
||||
|
||||
# Copy template configs to intermediary configs
|
||||
[[ -f "$ROOT_FOLDER/templates/env-sample" ]] && cp "$ROOT_FOLDER/templates/env-sample" "$ENV_FILE"
|
||||
[[ -f "$ROOT_FOLDER/templates/env-api-sample" ]] && cp "$ROOT_FOLDER/templates/env-api-sample" "$ENV_FILE_SYSTEM_API"
|
||||
|
||||
JWT_SECRET=$(derive_entropy "jwt")
|
||||
|
||||
for template in "${ENV_FILE}" "${ENV_FILE_SYSTEM_API}"; do
|
||||
for template in "${ENV_FILE}"; do
|
||||
sed -i "s/<dns_ip>/${DNS_IP}/g" "${template}"
|
||||
sed -i "s/<internal_ip>/${INTERNAL_IP}/g" "${template}"
|
||||
sed -i "s/<puid>/${PUID}/g" "${template}"
|
||||
|
@ -111,11 +105,16 @@ for template in "${ENV_FILE}" "${ENV_FILE_SYSTEM_API}"; do
|
|||
sed -i "s/<tz>/${TZ}/g" "${template}"
|
||||
sed -i "s/<jwt_secret>/${JWT_SECRET}/g" "${template}"
|
||||
sed -i "s/<root_folder>/${SED_ROOT_FOLDER}/g" "${template}"
|
||||
sed -i "s/<tipi_version>/$(cat "${ROOT_FOLDER}/VERSION")/g" "${template}"
|
||||
done
|
||||
|
||||
mv -f "$ENV_FILE" "$ROOT_FOLDER/.env"
|
||||
mv -f "$ENV_FILE_SYSTEM_API" "$ROOT_FOLDER/packages/system-api/.env"
|
||||
|
||||
# Run system-info.sh
|
||||
echo "Running system-info.sh..."
|
||||
bash "${ROOT_FOLDER}/scripts/system-info.sh"
|
||||
|
||||
# ansible-playbook ansible/start.yml -i ansible/hosts -K -e username="$USERNAME"
|
||||
|
||||
# Run docker-compose
|
||||
|
|
25
scripts/system-info.sh
Executable file
25
scripts/system-info.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e # Exit immediately if a command exits with a non-zero status.
|
||||
|
||||
ROOT_FOLDER="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
|
||||
STATE_FOLDER="${ROOT_FOLDER}/state"
|
||||
|
||||
# Available disk space
|
||||
TOTAL_DISK_SPACE_BYTES=$(df -P -B 1 / | tail -n 1 | awk '{print $2}')
|
||||
AVAILABLE_DISK_SPACE_BYTES=$(df -P -B 1 / | tail -n 1 | awk '{print $4}')
|
||||
USED_DISK_SPACE_BYTES=$(($TOTAL_DISK_SPACE_BYTES - $AVAILABLE_DISK_SPACE_BYTES))
|
||||
|
||||
# CPU info
|
||||
CPU_LOAD_PERCENTAGE=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
|
||||
|
||||
# Memory info
|
||||
MEM_TOTAL_BYTES=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}')
|
||||
MEM_FREE_BYTES=$(cat /proc/meminfo | grep MemFree | awk '{print $2}')
|
||||
MEM_USED_BYTES=$(($MEM_TOTAL_BYTES - $MEM_FREE_BYTES))
|
||||
|
||||
# Create temporary json file
|
||||
TEMP_JSON_FILE=$(mktemp)
|
||||
echo '{ "cpu": { "load": '"${CPU_LOAD_PERCENTAGE}"' }, "memory": { "total": '"${MEM_TOTAL_BYTES}"' , "used": '"${MEM_USED_BYTES}"', "available": '"${MEM_FREE_BYTES}"' }, "disk": { "total": '"${TOTAL_DISK_SPACE_BYTES}"' , "used": '"${USED_DISK_SPACE_BYTES}"', "available": '"${AVAILABLE_DISK_SPACE_BYTES}"' } }' > "${TEMP_JSON_FILE}"
|
||||
|
||||
# Write to state file
|
||||
echo "$(cat "${TEMP_JSON_FILE}")" > "${STATE_FOLDER}/system-info.json"
|
|
@ -1,4 +0,0 @@
|
|||
ROOT_FOLDER=<root_folder>
|
||||
JWT_SECRET=<jwt_secret>
|
||||
INTERNAL_IP=<internal_ip>
|
||||
ARCHITECTURE=<architecture>
|
|
@ -7,3 +7,5 @@ PGID=<pgid>
|
|||
INTERNAL_IP=<internal_ip>
|
||||
DNS_IP=<dns_ip>
|
||||
ARCHITECTURE=<architecture>
|
||||
TIPI_VERSION=<tipi_version>
|
||||
JWT_SECRET=<jwt_secret>
|
||||
|
|
Loading…
Add table
Reference in a new issue