mirror of
https://github.com/meienberger/runtipi.git
synced 2024-08-16 23:50:34 +03:00
WIP - New app store page
This commit is contained in:
parent
123aaee235
commit
f0f1da36ae
@ -1,4 +1,6 @@
|
||||
**/node_modules/
|
||||
**/.next/
|
||||
/node_modules/
|
||||
/.next/
|
||||
**/node_modules
|
||||
**/.next
|
||||
/node_modules
|
||||
/.next
|
||||
node_modules
|
||||
.next
|
||||
|
19
.github/workflows/release-candidate.yml
vendored
19
.github/workflows/release-candidate.yml
vendored
@ -61,22 +61,13 @@ jobs:
|
||||
- name: Build and push dashboard
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./packages/dashboard
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: meienberger/tipi-dashboard:rc-${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-dashboard:buildcache
|
||||
cache-to: type=registry,ref=meienberger/tipi-dashboard:buildcache,mode=max
|
||||
|
||||
- name: Build and push api
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./packages/system-api
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: meienberger/tipi-api:rc-${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-api:buildcache
|
||||
cache-to: type=registry,ref=meienberger/tipi-api:buildcache,mode=max
|
||||
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
|
||||
|
||||
|
||||
# Test installation script
|
||||
# test-install:
|
||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@ -62,16 +62,6 @@ jobs:
|
||||
context: ./packages/dashboard
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: meienberger/tipi-dashboard:latest,meienberger/tipi-dashboard:${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-dashboard:buildcache
|
||||
cache-to: type=registry,ref=meienberger/tipi-dashboard:buildcache,mode=max
|
||||
|
||||
- name: Build and push api
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./packages/system-api
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: meienberger/tipi-api:latest,meienberger/tipi-api:${{ steps.meta.outputs.TAG }}
|
||||
cache-from: type=registry,ref=meienberger/tipi-api:buildcache
|
||||
cache-to: type=registry,ref=meienberger/tipi-api: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
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ media/torrents/incomplete/*
|
||||
!media/torrents/incomplete/.gitkeep
|
||||
media/torrents/watch/*
|
||||
!media/torrents/watch/.gitkeep
|
||||
packages/dashboard/package-lock.json
|
||||
|
61
Dockerfile
Normal file
61
Dockerfile
Normal file
@ -0,0 +1,61 @@
|
||||
FROM node:18-buster-slim AS build
|
||||
|
||||
COPY ./packages/common /common
|
||||
|
||||
WORKDIR /api
|
||||
COPY ./packages/system-api/package.json /api/package.json
|
||||
RUN npm i
|
||||
COPY ./packages/system-api /api
|
||||
RUN npm run build
|
||||
|
||||
WORKDIR /dashboard
|
||||
COPY ./packages/dashboard/package.json /dashboard/package.json
|
||||
RUN npm i
|
||||
COPY ./packages/dashboard /dashboard
|
||||
RUN npm run build
|
||||
|
||||
|
||||
FROM ubuntu:20.04
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# Install docker
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release
|
||||
|
||||
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
RUN echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# Install node
|
||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN apt-get install -y nodejs
|
||||
RUN npm install --quiet node-gyp -g
|
||||
|
||||
# Install docker-compose
|
||||
RUN curl -L "https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
RUN chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
COPY ./packages/common /common
|
||||
|
||||
WORKDIR /api
|
||||
COPY ./packages/system-api/package.json /api/package.json
|
||||
RUN npm install --omit=dev
|
||||
|
||||
WORKDIR /dashboard
|
||||
COPY ./packages/dashboard/package.json /dashboard/package.json
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY --from=build /api /api
|
||||
COPY --from=build /dashboard /dashboard
|
||||
|
||||
WORKDIR /
|
43
Dockerfile.dev
Normal file
43
Dockerfile.dev
Normal file
@ -0,0 +1,43 @@
|
||||
FROM ubuntu:20.04
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# Install docker
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release
|
||||
|
||||
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
RUN echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# Install node
|
||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Install docker-compose
|
||||
RUN curl -L "https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
RUN chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
COPY ./packages/common /common
|
||||
|
||||
WORKDIR /api
|
||||
COPY ./packages/system-api/package.json /api/package.json
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /dashboard
|
||||
COPY ./packages/dashboard/package.json /dashboard/package.json
|
||||
RUN npm install
|
||||
|
||||
COPY ./packages/system-api /api
|
||||
COPY ./packages/dashboard /dashboard
|
||||
|
||||
WORKDIR /
|
@ -3,7 +3,7 @@
|
||||
"available": true,
|
||||
"port": 8104,
|
||||
"id": "adguard",
|
||||
"categories": ["network", "security"],
|
||||
"categories": ["network", "security", "featured"],
|
||||
"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": "ArneNaessens",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.5 KiB |
0
apps/anonaddy/metadata/description.md
Normal file
0
apps/anonaddy/metadata/description.md
Normal file
0
apps/calibre-web/metadata/description.md
Normal file
0
apps/calibre-web/metadata/description.md
Normal file
0
apps/code-server/metadata/description.md
Normal file
0
apps/code-server/metadata/description.md
Normal file
0
apps/filebrowser/metadata/description.md
Normal file
0
apps/filebrowser/metadata/description.md
Normal file
0
apps/filerun/metadata/description.md
Normal file
0
apps/filerun/metadata/description.md
Normal file
0
apps/freshrss/metadata/description.md
Normal file
0
apps/freshrss/metadata/description.md
Normal file
0
apps/gitea/metadata/description.md
Normal file
0
apps/gitea/metadata/description.md
Normal file
0
apps/homarr/metadata/description.md
Normal file
0
apps/homarr/metadata/description.md
Normal file
0
apps/homeassistant/metadata/description.md
Normal file
0
apps/homeassistant/metadata/description.md
Normal file
0
apps/invidious/metadata/description.md
Normal file
0
apps/invidious/metadata/description.md
Normal file
0
apps/jackett/metadata/description.md
Normal file
0
apps/jackett/metadata/description.md
Normal file
0
apps/jellyfin/metadata/description.md
Normal file
0
apps/jellyfin/metadata/description.md
Normal file
0
apps/joplin/metadata/description.md
Normal file
0
apps/joplin/metadata/description.md
Normal file
0
apps/libreddit/metadata/description.md
Normal file
0
apps/libreddit/metadata/description.md
Normal file
0
apps/n8n/metadata/description.md
Normal file
0
apps/n8n/metadata/description.md
Normal file
0
apps/nextcloud/metadata/description.md
Normal file
0
apps/nextcloud/metadata/description.md
Normal file
0
apps/nitter/metadata/description.md
Normal file
0
apps/nitter/metadata/description.md
Normal file
0
apps/nodered/metadata/description.md
Normal file
0
apps/nodered/metadata/description.md
Normal file
0
apps/photoprism/metadata/description.md
Normal file
0
apps/photoprism/metadata/description.md
Normal file
0
apps/pihole/metadata/description.md
Normal file
0
apps/pihole/metadata/description.md
Normal file
0
apps/prowlarr/metadata/description.md
Normal file
0
apps/prowlarr/metadata/description.md
Normal file
0
apps/radarr/metadata/description.md
Normal file
0
apps/radarr/metadata/description.md
Normal file
0
apps/simple-torrent/metadata/description.md
Normal file
0
apps/simple-torrent/metadata/description.md
Normal file
0
apps/sonarr/metadata/description.md
Normal file
0
apps/sonarr/metadata/description.md
Normal file
0
apps/syncthing/metadata/description.md
Normal file
0
apps/syncthing/metadata/description.md
Normal file
@ -3,6 +3,7 @@
|
||||
"available": true,
|
||||
"port": 8093,
|
||||
"id": "tailscale",
|
||||
"categories": ["featured"],
|
||||
"description": "Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.",
|
||||
"short_desc": "The easiest, most secure way to use WireGuard and 2FA.",
|
||||
"author": "© Tailscale Inc.",
|
||||
|
0
apps/tailscale/metadata/description.md
Normal file
0
apps/tailscale/metadata/description.md
Normal file
0
apps/tautulli/metadata/description.md
Normal file
0
apps/tautulli/metadata/description.md
Normal file
0
apps/transmission/metadata/description.md
Normal file
0
apps/transmission/metadata/description.md
Normal file
0
apps/ttyd/metadata/description.md
Normal file
0
apps/ttyd/metadata/description.md
Normal file
0
apps/vaultwarden/metadata/description.md
Normal file
0
apps/vaultwarden/metadata/description.md
Normal file
0
apps/wg-easy/metadata/description.md
Normal file
0
apps/wg-easy/metadata/description.md
Normal file
@ -3,8 +3,9 @@ version: "3.7"
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: ./packages/system-api
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
command: bash -c "cd /api && npm run dev"
|
||||
container_name: api
|
||||
ports:
|
||||
- 3001:3001
|
||||
@ -12,8 +13,8 @@ services:
|
||||
## Docker sock
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ${PWD}:/tipi
|
||||
- ${PWD}/packages/system-api:/app
|
||||
- /app/node_modules
|
||||
- ${PWD}/packages/system-api:/api
|
||||
- /api/node_modules
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
- TIPI_VERSION=${TIPI_VERSION}
|
||||
@ -25,8 +26,9 @@ services:
|
||||
|
||||
dashboard:
|
||||
build:
|
||||
context: ./packages/dashboard
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
command: bash -c "cd /dashboard && npm run dev"
|
||||
container_name: dashboard
|
||||
ports:
|
||||
- 3000:3000
|
||||
@ -35,9 +37,9 @@ services:
|
||||
environment:
|
||||
- INTERNAL_IP=${INTERNAL_IP}
|
||||
volumes:
|
||||
- ${PWD}/packages/dashboard:/app
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
- ${PWD}/packages/dashboard:/dashboard
|
||||
- /dashboard/node_modules
|
||||
- /dashboard/.next
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.dashboard.rule: PathPrefix("/") # Host(`tipi.local`) &&
|
||||
|
@ -16,7 +16,8 @@ services:
|
||||
- tipi_main_network
|
||||
|
||||
api:
|
||||
image: meienberger/tipi-api:${TIPI_VERSION}
|
||||
image: meienberger/runtipi:${TIPI_VERSION}
|
||||
command: bash -c "cd /api && npm run start"
|
||||
restart: unless-stopped
|
||||
container_name: api
|
||||
ports:
|
||||
@ -35,7 +36,8 @@ services:
|
||||
- tipi_main_network
|
||||
|
||||
dashboard:
|
||||
image: meienberger/tipi-dashboard:${TIPI_VERSION}
|
||||
image: meienberger/runtipi:${TIPI_VERSION}
|
||||
command: bash -c "cd /dasboard && npm run start"
|
||||
restart: unless-stopped
|
||||
container_name: dashboard
|
||||
ports:
|
||||
|
@ -30,5 +30,6 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/meienberger/runtipi/issues"
|
||||
},
|
||||
"homepage": "https://github.com/meienberger/runtipi#readme"
|
||||
"homepage": "https://github.com/meienberger/runtipi#readme",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@runtipi/common",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"main": "./dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"test": "jest --coverage --passWithNoTests",
|
||||
"build": "tsc -b tsconfig.build.json"
|
||||
},
|
||||
"author": "",
|
||||
|
@ -10,4 +10,5 @@ export const APP_CATEGORIES = [
|
||||
{ name: 'Utilities', id: AppCategoriesEnum.UTILITIES, icon: 'FaWrench' },
|
||||
{ name: 'Photography', id: AppCategoriesEnum.PHOTOGRAPHY, icon: 'FaCamera' },
|
||||
{ name: 'Security', id: AppCategoriesEnum.SECURITY, icon: 'FaShieldAlt' },
|
||||
{ name: 'Featured', id: AppCategoriesEnum.FEATURED, icon: 'FaStar' },
|
||||
];
|
||||
|
@ -7,6 +7,7 @@ export enum AppCategoriesEnum {
|
||||
UTILITIES = 'utilities',
|
||||
PHOTOGRAPHY = 'photography',
|
||||
SECURITY = 'security',
|
||||
FEATURED = 'featured',
|
||||
}
|
||||
|
||||
export enum FieldTypes {
|
||||
|
@ -1,3 +1,4 @@
|
||||
*.config.js
|
||||
.eslintrc.js
|
||||
next.config.js
|
||||
next.config.js
|
||||
jest.config.js
|
||||
|
@ -1,13 +0,0 @@
|
||||
FROM node:18-buster-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./package.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN npm run build
|
||||
|
||||
CMD ["npm", "run", "start"]
|
@ -1,11 +0,0 @@
|
||||
FROM node:18
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./package.json ./
|
||||
|
||||
RUN yarn
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
CMD ["yarn", "dev"]
|
12
packages/dashboard/jest.config.js
Normal file
12
packages/dashboard/jest.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
verbose: true,
|
||||
// testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
// setupFiles: ['<rootDir>/tests/dotenv-config.ts'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
||||
coverageProvider: 'v8',
|
||||
passWithNoTests: true,
|
||||
};
|
@ -3,6 +3,7 @@
|
||||
"version": "0.2.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "jest --colors",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
@ -13,7 +14,7 @@
|
||||
"@emotion/react": "^11",
|
||||
"@emotion/styled": "^11",
|
||||
"@fontsource/open-sans": "^4.5.8",
|
||||
"@runtipi/common": "^0.2.7",
|
||||
"@runtipi/common": "file:../common",
|
||||
"axios": "^0.26.1",
|
||||
"clsx": "^1.1.1",
|
||||
"final-form": "^4.20.6",
|
||||
@ -36,6 +37,7 @@
|
||||
"@types/node": "17.0.31",
|
||||
"@types/react": "18.0.8",
|
||||
"@types/react-dom": "18.0.3",
|
||||
"@types/react-slick": "^0.23.8",
|
||||
"@types/validator": "^13.7.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.18.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { AiOutlineDashboard, AiOutlineSetting, AiOutlineAppstore } from 'react-icons/ai';
|
||||
import { FaRegMoon } from 'react-icons/fa';
|
||||
import { FaAppStore, FaRegMoon } from 'react-icons/fa';
|
||||
import { FiLogOut } from 'react-icons/fi';
|
||||
import Package from '../../../package.json';
|
||||
import { Box, Divider, Flex, List, ListItem, Switch, useColorMode } from '@chakra-ui/react';
|
||||
@ -45,7 +45,8 @@ const SideMenu: React.FC = () => {
|
||||
<img className="self-center mb-5 logo mt-0 md:mt-5" src="/tipi.png" width={512} height={512} />
|
||||
<List spacing={3} className="pt-5">
|
||||
{renderMenuItem('Dashboard', '', AiOutlineDashboard)}
|
||||
{renderMenuItem('Apps', 'apps', AiOutlineAppstore)}
|
||||
{renderMenuItem('My Apps', 'apps', AiOutlineAppstore)}
|
||||
{renderMenuItem('App Store', 'app-store', FaAppStore)}
|
||||
{renderMenuItem('Settings', 'settings', AiOutlineSetting)}
|
||||
</List>
|
||||
<Divider className="my-3" />
|
||||
|
@ -0,0 +1,25 @@
|
||||
import { AppConfig } from '@runtipi/common';
|
||||
import React from 'react';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import FeaturedCard from './FeaturedCard';
|
||||
|
||||
interface IProps {
|
||||
apps: AppConfig[];
|
||||
}
|
||||
|
||||
const FeaturedApps: React.FC<IProps> = ({ apps }) => {
|
||||
const [appIndex, setAppIndex] = React.useState(0);
|
||||
|
||||
return (
|
||||
<Flex className="flex-col relative">
|
||||
<Box className="relative mb-3" height={200}>
|
||||
{apps.map((app, index) => {
|
||||
return <FeaturedCard show={index === appIndex} key={app.id} app={app} />;
|
||||
})}
|
||||
</Box>
|
||||
<Button onClick={() => setAppIndex(1)}>Next</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeaturedApps;
|
@ -0,0 +1,36 @@
|
||||
import { Flex, ScaleFade } from '@chakra-ui/react';
|
||||
import { AppConfig } from '@runtipi/common';
|
||||
import React from 'react';
|
||||
|
||||
interface IProps {
|
||||
app: AppConfig;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
const FeaturedCard: React.FC<IProps> = ({ app, show }) => {
|
||||
return (
|
||||
<ScaleFade initialScale={0.9} in={show}>
|
||||
<Flex
|
||||
className="overflow-hidden absolute left-0 right-0 border-2"
|
||||
height={200}
|
||||
rounded="md"
|
||||
shadow="md"
|
||||
style={{
|
||||
backgroundImage: `url(https://images.unsplash.com/photo-1488590528505-98d2b5aba04b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80)`,
|
||||
}}
|
||||
>
|
||||
<div className="relative flex flex-1 w-max lg:bg-gradient-to-r from-white via-white">
|
||||
<div className="flex absolute bottom-0 flex-row p-3">
|
||||
<img src={app.image} width={80} height={80} className="rounded-lg mr-2" />
|
||||
<div className="self-end mb-1">
|
||||
<div className="font-bold text-xl">{app.name}</div>
|
||||
<div className="text-md">{app.short_desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Flex>
|
||||
</ScaleFade>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeaturedCard;
|
@ -0,0 +1,24 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { AppCategoriesEnum } from '@runtipi/common';
|
||||
import React from 'react';
|
||||
import { useAppsStore } from '../../../state/appsStore';
|
||||
import FeaturedApps from '../components/FeaturedApps';
|
||||
|
||||
function nonNullable<T>(value: T): value is NonNullable<T> {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
const AppStoreContainer = () => {
|
||||
const { apps } = useAppsStore();
|
||||
|
||||
const featuredApps = apps.map((app) => (app.categories?.includes(AppCategoriesEnum.FEATURED) ? app : null)).filter(nonNullable);
|
||||
|
||||
return (
|
||||
<Flex className="flex-col">
|
||||
<h1 className="font-bold text-3xl mb-5">App Store</h1>
|
||||
<FeaturedApps apps={featuredApps} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppStoreContainer;
|
22
packages/dashboard/src/pages/app-store/index.tsx
Normal file
22
packages/dashboard/src/pages/app-store/index.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import type { NextPage } from 'next';
|
||||
import Layout from '../../components/Layout';
|
||||
import AppStoreContainer from '../../modules/AppStore/containers/AppStoreContainer';
|
||||
import { useAppsStore } from '../../state/appsStore';
|
||||
import { RequestStatus } from '../../core/types';
|
||||
|
||||
const Apps: NextPage = () => {
|
||||
const { fetch, status } = useAppsStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return (
|
||||
<Layout loading={status === RequestStatus.LOADING}>
|
||||
<AppStoreContainer />
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Apps;
|
5
packages/system-api/.gitignore
vendored
5
packages/system-api/.gitignore
vendored
@ -1,2 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
dist/
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
@ -1,3 +1,15 @@
|
||||
### BUILD ###
|
||||
FROM node:18-buster-slim AS build
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./package.json ./
|
||||
|
||||
RUN npm install --quiet node-gyp -g
|
||||
RUN npm install
|
||||
|
||||
RUN npm run build
|
||||
|
||||
### MAIN ###
|
||||
FROM ubuntu:20.04
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -33,10 +45,8 @@ RUN chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
COPY ./package.json ./
|
||||
|
||||
RUN npm install
|
||||
RUN npm install --production
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN npm run build
|
||||
COPY --from=build /app/dist /app/dist
|
||||
|
||||
CMD ["npm", "run", "start"]
|
@ -1,7 +1,12 @@
|
||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
verbose: true,
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
setupFiles: ['<rootDir>/tests/dotenv-config.ts'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
||||
coverageProvider: 'v8',
|
||||
passWithNoTests: true,
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"test": "jest",
|
||||
"test": "jest --colors",
|
||||
"test:watch": "jest --watch",
|
||||
"build": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --minify --analyze=verbose --external:./node_modules/* --format=esm",
|
||||
"build:watch": "esbuild --bundle src/server.ts --outdir=dist --allow-overwrite --sourcemap --platform=node --external:./node_modules/* --format=esm --watch",
|
||||
@ -21,7 +21,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@runtipi/common": "^0.2.7",
|
||||
"@runtipi/common": "file:../common",
|
||||
"argon2": "^0.28.5",
|
||||
"axios": "^0.26.1",
|
||||
"compression": "^1.7.4",
|
||||
|
@ -43,14 +43,18 @@ const testApp3: Partial<AppConfig> = {
|
||||
|
||||
const MOCK_FILE_EMPTY = {
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/config.json`]: JSON.stringify(testApp),
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/metadata/description.md`]: 'md desc',
|
||||
[`${config.ROOT_FOLDER}/.env`]: 'TEST=test',
|
||||
[`${config.ROOT_FOLDER}/state/apps.json`]: '{"installed": ""}',
|
||||
};
|
||||
|
||||
const MOCK_FILE_INSTALLED = {
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/config.json`]: JSON.stringify(testApp),
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/metadata/description.md`]: 'md desc',
|
||||
[`${config.ROOT_FOLDER}/apps/test-app2/config.json`]: JSON.stringify(testApp2),
|
||||
[`${config.ROOT_FOLDER}/apps/test-app2/metadata/description.md`]: 'md desc',
|
||||
[`${config.ROOT_FOLDER}/apps/test-app3/config.json`]: JSON.stringify(testApp3),
|
||||
[`${config.ROOT_FOLDER}/apps/test-app3/metadata/description.md`]: 'md desc',
|
||||
[`${config.ROOT_FOLDER}/.env`]: 'TEST=test',
|
||||
[`${config.ROOT_FOLDER}/state/apps.json`]: '{"installed": "test-app"}',
|
||||
[`${config.ROOT_FOLDER}/app-data/test-app`]: '',
|
||||
@ -253,8 +257,8 @@ describe('List apps', () => {
|
||||
const apps = await AppsService.listApps();
|
||||
|
||||
expect(apps).toEqual([
|
||||
{ ...testApp, installed: true, status: 'stopped' },
|
||||
{ ...testApp2, installed: false, status: 'stopped' },
|
||||
{ ...testApp, installed: true, status: 'stopped', description: 'md desc' },
|
||||
{ ...testApp2, installed: false, status: 'stopped', description: 'md desc' },
|
||||
]);
|
||||
expect(apps.length).toBe(2);
|
||||
expect(apps[0].id).toBe('test-app');
|
||||
|
@ -48,7 +48,7 @@ const listApps = async (): Promise<AppConfig[]> => {
|
||||
.map((app) => {
|
||||
try {
|
||||
return readJsonFile(`/apps/${app}/config.json`);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
@ -62,7 +62,7 @@ const listApps = async (): Promise<AppConfig[]> => {
|
||||
apps.forEach((app) => {
|
||||
app.installed = installed.includes(app.id);
|
||||
app.status = (dockerContainers.find((container) => container.name === `${app.id}`)?.state as AppStatusEnum) || AppStatusEnum.STOPPED;
|
||||
app.description = readFile(`/apps/${app.id}/description.md`);
|
||||
app.description = readFile(`/apps/${app.id}/metadata/description.md`);
|
||||
});
|
||||
|
||||
return apps;
|
||||
|
@ -94,7 +94,7 @@ describe('Me', () => {
|
||||
it('Should return user if present in request', async () => {
|
||||
const json = jest.fn();
|
||||
const res = { status: jest.fn(() => ({ json })) } as unknown as Response;
|
||||
const req = { user } as Request;
|
||||
const req = { user } as unknown as Request;
|
||||
|
||||
await AuthController.me(req, res, next);
|
||||
|
||||
@ -137,7 +137,7 @@ describe('isConfigured', () => {
|
||||
|
||||
const json = jest.fn();
|
||||
const res = { status: jest.fn(() => ({ json })) } as unknown as Response;
|
||||
const req = { user } as Request;
|
||||
const req = { user } as unknown as Request;
|
||||
|
||||
await AuthController.isConfigured(req, res, next);
|
||||
|
||||
|
@ -26,10 +26,6 @@ const register = async (email: string, password: string, name: string) => {
|
||||
throw new Error('Missing email or password');
|
||||
}
|
||||
|
||||
if (users.find((user) => user.email === email)) {
|
||||
throw new Error('User already exists');
|
||||
}
|
||||
|
||||
const hash = await argon2.hash(password);
|
||||
const newuser: IUser = { email, name, password: hash };
|
||||
|
||||
|
@ -37,11 +37,12 @@ importers:
|
||||
'@emotion/react': ^11
|
||||
'@emotion/styled': ^11
|
||||
'@fontsource/open-sans': ^4.5.8
|
||||
'@runtipi/common': ^0.2.1
|
||||
'@runtipi/common': file:../common
|
||||
'@types/js-cookie': ^3.0.2
|
||||
'@types/node': 17.0.31
|
||||
'@types/react': 18.0.8
|
||||
'@types/react-dom': 18.0.3
|
||||
'@types/react-slick': ^0.23.8
|
||||
'@types/validator': ^13.7.2
|
||||
'@typescript-eslint/eslint-plugin': ^5.18.0
|
||||
'@typescript-eslint/parser': ^5.0.0
|
||||
@ -59,6 +60,7 @@ importers:
|
||||
next: 12.1.6
|
||||
postcss: ^8.4.12
|
||||
react: 18.1.0
|
||||
react-awesome-slider: ^4.1.0
|
||||
react-dom: 18.1.0
|
||||
react-final-form: ^6.5.9
|
||||
react-icons: ^4.3.1
|
||||
@ -73,7 +75,7 @@ importers:
|
||||
'@emotion/react': 11.9.0_4mdsreeeydipjms3kbrjyybtve
|
||||
'@emotion/styled': 11.8.1_tnefweo2a67ybg6wfzi6ieqilm
|
||||
'@fontsource/open-sans': 4.5.8
|
||||
'@runtipi/common': link:../common
|
||||
'@runtipi/common': file:packages/common
|
||||
axios: 0.26.1
|
||||
clsx: 1.1.1
|
||||
final-form: 4.20.7
|
||||
@ -82,6 +84,7 @@ importers:
|
||||
js-cookie: 3.0.1
|
||||
next: 12.1.6_talmm3uuvp6ssixt2qevhfgvue
|
||||
react: 18.1.0
|
||||
react-awesome-slider: 4.1.0
|
||||
react-dom: 18.1.0_react@18.1.0
|
||||
react-final-form: 6.5.9_bnxchjdfy45cdln7bu7hnhf37u
|
||||
react-icons: 4.3.1_react@18.1.0
|
||||
@ -95,6 +98,7 @@ importers:
|
||||
'@types/node': 17.0.31
|
||||
'@types/react': 18.0.8
|
||||
'@types/react-dom': 18.0.3
|
||||
'@types/react-slick': 0.23.8
|
||||
'@types/validator': 13.7.2
|
||||
'@typescript-eslint/eslint-plugin': 5.22.0_oztpoyrbzkyaikrhdkppp3gagu
|
||||
'@typescript-eslint/parser': 5.22.0_uhoeudlwl7kc47h4kncsfowede
|
||||
@ -109,7 +113,7 @@ importers:
|
||||
|
||||
packages/system-api:
|
||||
specifiers:
|
||||
'@runtipi/common': ^0.2.2
|
||||
'@runtipi/common': file:../common
|
||||
'@types/compression': ^1.7.2
|
||||
'@types/cookie-parser': ^1.4.3
|
||||
'@types/cors': ^2.8.12
|
||||
@ -157,7 +161,7 @@ importers:
|
||||
ts-jest: ^28.0.2
|
||||
typescript: 4.6.4
|
||||
dependencies:
|
||||
'@runtipi/common': link:../common
|
||||
'@runtipi/common': file:packages/common
|
||||
argon2: 0.28.5
|
||||
axios: 0.26.1
|
||||
compression: 1.7.4
|
||||
@ -2243,6 +2247,12 @@ packages:
|
||||
'@types/react': 18.0.8
|
||||
dev: true
|
||||
|
||||
/@types/react-slick/0.23.8:
|
||||
resolution: {integrity: sha512-SfzSg++/3uyftVZaCgHpW+2fnJFsyJEQ/YdsuqfOWQ5lqUYV/gY/UwAnkw4qksCj5jalto/T5rKXJ8zeFldQeA==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.8
|
||||
dev: true
|
||||
|
||||
/@types/react/18.0.8:
|
||||
resolution: {integrity: sha512-+j2hk9BzCOrrOSJASi5XiOyBbERk9jG5O73Ya4M0env5Ixi6vUNli4qy994AINcEF+1IEHISYFfIT4zwr++LKw==}
|
||||
dependencies:
|
||||
@ -4045,7 +4055,7 @@ packages:
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
|
||||
'@typescript-eslint/parser': 5.22.0_uhoeudlwl7kc47h4kncsfowede
|
||||
debug: 3.2.7
|
||||
eslint-import-resolver-node: 0.3.6
|
||||
find-up: 2.1.0
|
||||
@ -6806,6 +6816,12 @@ packages:
|
||||
strip-json-comments: 2.0.1
|
||||
dev: true
|
||||
|
||||
/react-awesome-slider/4.1.0:
|
||||
resolution: {integrity: sha512-cbPI1MTpVLKbEH6gf9bwtJb8Ja6R/YJonKbUQehfq2B2AAJkgDMeHntaa4SgGCRqWd55xKiT+CkjfKau1QRsKw==}
|
||||
dependencies:
|
||||
web-animation-club: 0.6.0
|
||||
dev: false
|
||||
|
||||
/react-clientside-effect/1.2.6_react@18.1.0:
|
||||
resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==}
|
||||
peerDependencies:
|
||||
@ -7759,6 +7775,10 @@ packages:
|
||||
makeerror: 1.0.12
|
||||
dev: true
|
||||
|
||||
/web-animation-club/0.6.0:
|
||||
resolution: {integrity: sha512-9W+EQu1HiaPLe/7WZlhJ2ULqQ4VL80RPDYW+ZcjfTKp6ayOuT1k3SVO6+tu0VBRmOqueJ/mrG+rjjInIv8Aglg==}
|
||||
dev: false
|
||||
|
||||
/webidl-conversions/3.0.1:
|
||||
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
|
||||
dev: false
|
||||
@ -7903,3 +7923,9 @@ packages:
|
||||
dependencies:
|
||||
react: 18.1.0
|
||||
dev: false
|
||||
|
||||
file:packages/common:
|
||||
resolution: {directory: packages/common, type: directory}
|
||||
name: '@runtipi/common'
|
||||
version: 0.2.8
|
||||
dev: false
|
||||
|
Loading…
Reference in New Issue
Block a user