App resolver

This commit is contained in:
Nicolas Meienberger 2022-06-16 22:15:16 +02:00
parent 58ca7709ba
commit 6715880c01
10 changed files with 199 additions and 57 deletions

View file

@ -53,7 +53,7 @@ export interface AppConfig {
description: string;
version: string;
image: string;
form_fields: Record<string, FormField>;
form_fields: FormField[];
short_desc: string;
author: string;
source: string;

View file

@ -14,5 +14,7 @@ module.exports = {
'no-restricted-exports': 0,
'max-len': [1, { code: 200 }],
'import/extensions': ['error', 'ignorePackages', { js: 'never', jsx: 'never', ts: 'never', tsx: 'never' }],
indent: 'off',
'@typescript-eslint/indent': 0,
},
};

View file

@ -29,6 +29,7 @@
"cors": "^2.8.5",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"graphql": "^16.5.0",
"helmet": "^5.0.2",
"internal-ip": "^6.0.0",
"jsonwebtoken": "^8.5.1",
@ -41,7 +42,8 @@
"passport-http-bearer": "^1.0.1",
"public-ip": "^5.0.0",
"systeminformation": "^5.11.9",
"tcp-port-used": "^1.0.2"
"tcp-port-used": "^1.0.2",
"type-graphql": "^1.1.1"
},
"devDependencies": {
"@types/compression": "^1.7.2",

View file

@ -18,4 +18,4 @@ export const appNames = [
'homarr',
'code-server',
'calibre-web',
];
] as const;

View file

@ -0,0 +1,11 @@
import { Query, Resolver } from 'type-graphql';
import AppsService from './apps.service';
import { ListAppsResonse } from './apps.types';
@Resolver()
export default class AppsResolver {
@Query(() => ListAppsResonse)
getChannels(): Promise<ListAppsResonse> {
return AppsService.listApps();
}
}

View file

@ -2,6 +2,7 @@ import si from 'systeminformation';
import { AppConfig, AppStatusEnum } from '@runtipi/common';
import { createFolder, fileExists, readFile, readJsonFile } from '../fs/fs.helpers';
import { checkAppExists, checkAppRequirements, checkEnvFile, ensureAppState, generateEnvFile, getAvailableApps, getInitalFormValues, getStateFile, runAppScript } from './apps.helpers';
import { ListAppsResonse } from './apps.types';
const startApp = async (appName: string): Promise<void> => {
checkAppExists(appName);
@ -43,7 +44,7 @@ const installApp = async (id: string, form: Record<string, string>): Promise<voi
return Promise.resolve();
};
const listApps = async (): Promise<AppConfig[]> => {
const listApps = async (): Promise<ListAppsResonse> => {
const apps: AppConfig[] = getAvailableApps()
.map((app) => {
try {
@ -65,7 +66,7 @@ const listApps = async (): Promise<AppConfig[]> => {
app.description = readFile(`/apps/${app.id}/metadata/description.md`);
});
return apps;
return { apps };
};
const getAppInfo = async (id: string): Promise<AppConfig> => {

View file

@ -0,0 +1,82 @@
import { AppCategoriesEnum, AppStatusEnum, FieldTypes } from '@runtipi/common';
import { Field, ObjectType } from 'type-graphql';
@ObjectType()
class FormField {
@Field()
type!: FieldTypes;
@Field()
label!: string;
@Field({ nullable: true })
max?: number;
@Field({ nullable: true })
min?: number;
@Field({ nullable: true })
hint?: string;
@Field({ nullable: true })
required?: boolean;
@Field()
env_variable!: string;
}
@ObjectType()
class App {
@Field(() => String)
id!: string;
@Field(() => Boolean)
available!: boolean;
@Field(() => Number)
port!: number;
@Field(() => String)
name!: string;
@Field(() => String)
description!: string;
@Field(() => String, { nullable: true })
version?: string;
@Field(() => String)
image!: string;
@Field(() => String)
short_desc!: string;
@Field(() => String)
author!: string;
@Field(() => String)
source!: string;
@Field(() => Boolean)
installed!: boolean;
@Field(() => [AppCategoriesEnum])
categories!: AppCategoriesEnum[];
@Field(() => AppStatusEnum)
status!: AppStatusEnum;
@Field(() => String, { nullable: true })
url_suffix?: string;
@Field(() => [FormField])
form_fields?: FormField[];
}
@ObjectType()
class ListAppsResonse {
@Field(() => [App])
apps!: App[];
}
export { ListAppsResonse, App };

View file

@ -0,0 +1,11 @@
import { GraphQLSchema } from 'graphql';
import { buildSchema } from 'type-graphql';
import AppsResolver from './modules/apps/apps.resolver.ts';
const createSchema = (): Promise<GraphQLSchema> =>
buildSchema({
resolvers: [AppsResolver],
validate: true,
});
export { createSchema };

View file

@ -13,7 +13,8 @@
"resolveJsonModule": true,
"isolatedModules": false,
"jsx": "preserve",
"incremental": true
"incremental": true,
"experimentalDecorators": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "jest.config.cjs"],
"exclude": ["node_modules"]

134
pnpm-lock.yaml generated
View file

@ -153,6 +153,7 @@ importers:
eslint-plugin-import: ^2.26.0
eslint-plugin-prettier: ^4.0.0
express: ^4.17.3
graphql: ^16.5.0
helmet: ^5.0.2
internal-ip: ^6.0.0
jest: ^28.1.0
@ -171,6 +172,7 @@ importers:
systeminformation: ^5.11.9
tcp-port-used: ^1.0.2
ts-jest: ^28.0.2
type-graphql: ^1.1.1
typescript: 4.6.4
dependencies:
'@runtipi/common': file:packages/common
@ -181,6 +183,7 @@ importers:
cors: 2.8.5
dotenv: 16.0.0
express: 4.18.1
graphql: 16.5.0
helmet: 5.0.2
internal-ip: 6.2.0
jsonwebtoken: 8.5.1
@ -194,6 +197,7 @@ importers:
public-ip: 5.0.0
systeminformation: 5.11.14
tcp-port-used: 1.0.2
type-graphql: 1.1.1_graphql@16.5.0
devDependencies:
'@types/compression': 1.7.2
'@types/cookie-parser': 1.4.3
@ -213,7 +217,7 @@ importers:
eslint: 8.15.0
eslint-config-airbnb-typescript: 17.0.0_c2ouaf3l4ivgkc6ae4nebvztom
eslint-config-prettier: 8.5.0_eslint@8.15.0
eslint-plugin-import: 2.26.0_6nacgdzqm4zbhelsxkmd2vkvxy
eslint-plugin-import: 2.26.0_eslint@8.15.0
eslint-plugin-prettier: 4.0.0_iqftbjqlxzn3ny5nablrkczhqi
jest: 28.1.0
nodemon: 2.0.16
@ -2123,6 +2127,13 @@ packages:
'@types/serve-static': 1.13.10
dev: true
/@types/glob/7.2.0:
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
dependencies:
'@types/minimatch': 3.0.5
'@types/node': 17.0.31
dev: false
/@types/graceful-fs/4.1.5:
resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
dependencies:
@ -2248,6 +2259,10 @@ packages:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true
/@types/minimatch/3.0.5:
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
dev: false
/@types/mock-fs/4.13.1:
resolution: {integrity: sha512-m6nFAJ3lBSnqbvDZioawRvpLXSaPyn52Srf7OfzjubYbYX8MTUdIgDxQl0wEapm4m/pNYSd9TXocpQ0TvZFlYA==}
dependencies:
@ -2327,6 +2342,10 @@ packages:
/@types/scheduler/0.16.2:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
/@types/semver/7.3.10:
resolution: {integrity: sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==}
dev: false
/@types/serve-static/1.13.10:
resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
dependencies:
@ -2974,8 +2993,6 @@ packages:
raw-body: 2.5.1
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
dev: false
/boxen/5.1.2:
@ -3287,8 +3304,6 @@ packages:
on-headers: 1.0.2
safe-buffer: 5.1.2
vary: 1.1.2
transitivePeerDependencies:
- supports-color
dev: false
/compute-scroll-into-view/1.0.14:
@ -3441,37 +3456,15 @@ packages:
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.0.0
/debug/3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
dev: true
/debug/3.2.7_supports-color@5.5.0:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
supports-color: 5.5.0
dev: true
/debug/4.3.1:
resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==}
engines: {node: '>=6.0'}
@ -4018,7 +4011,7 @@ packages:
dependencies:
confusing-browser-globals: 1.0.11
eslint: 8.15.0
eslint-plugin-import: 2.26.0_6nacgdzqm4zbhelsxkmd2vkvxy
eslint-plugin-import: 2.26.0_eslint@8.15.0
object.assign: 4.1.2
object.entries: 1.1.5
semver: 6.3.0
@ -4051,7 +4044,7 @@ packages:
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
eslint: 8.15.0
eslint-config-airbnb-base: 15.0.0_gwd37gqv3vjv3xlpl7ju3ag2qu
eslint-plugin-import: 2.26.0_6nacgdzqm4zbhelsxkmd2vkvxy
eslint-plugin-import: 2.26.0_eslint@8.15.0
dev: true
/eslint-config-airbnb-typescript/17.0.0_r46exuh3jlhq2wmrnqx2ufqspa:
@ -4119,8 +4112,6 @@ packages:
dependencies:
debug: 3.2.7
resolve: 1.22.0
transitivePeerDependencies:
- supports-color
dev: true
/eslint-import-resolver-typescript/2.4.0_l3k33lf43msdtqtpwrwceacqke:
@ -4141,6 +4132,14 @@ packages:
- supports-color
dev: true
/eslint-module-utils/2.7.3:
resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==}
engines: {node: '>=4'}
dependencies:
debug: 3.2.7
find-up: 2.1.0
dev: true
/eslint-module-utils/2.7.3_sysdrzuw2ki4kxpuwc4tznw2ha:
resolution: {integrity: sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==}
engines: {node: '>=4'}
@ -4225,24 +4224,19 @@ packages:
- supports-color
dev: true
/eslint-plugin-import/2.26.0_6nacgdzqm4zbhelsxkmd2vkvxy:
/eslint-plugin-import/2.26.0_eslint@8.15.0:
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
array-includes: 3.1.5
array.prototype.flat: 1.3.0
debug: 2.6.9
doctrine: 2.1.0
eslint: 8.15.0
eslint-import-resolver-node: 0.3.6
eslint-module-utils: 2.7.3_wex3ustmkv4ospy3s77r6ihlwq
eslint-module-utils: 2.7.3
has: 1.0.3
is-core-module: 2.9.0
is-glob: 4.0.3
@ -4250,10 +4244,6 @@ packages:
object.values: 1.1.5
resolve: 1.22.0
tsconfig-paths: 3.14.1
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
/eslint-plugin-import/2.26.0_hhyjdrupy4c2vgtpytri6cjwoy:
@ -4616,8 +4606,6 @@ packages:
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
transitivePeerDependencies:
- supports-color
dev: false
/extend/3.0.2:
@ -4700,8 +4688,6 @@ packages:
parseurl: 1.3.3
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
dev: false
/find-root/1.1.0:
@ -5018,6 +5004,29 @@ packages:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
dev: true
/graphql-query-complexity/0.7.2_graphql@16.5.0:
resolution: {integrity: sha512-+VgmrfxGEjHI3zuojWOR8bsz7Ycz/BZjNjxnlUieTz5DsB92WoIrYCSZdWG7UWZ3rfcA1Gb2Nf+wB80GsaZWuQ==}
peerDependencies:
graphql: ^0.13.0 || ^14.0.0 || ^15.0.0
dependencies:
graphql: 16.5.0
lodash.get: 4.4.2
dev: false
/graphql-subscriptions/1.2.1_graphql@16.5.0:
resolution: {integrity: sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g==}
peerDependencies:
graphql: ^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0
dependencies:
graphql: 16.5.0
iterall: 1.3.0
dev: false
/graphql/16.5.0:
resolution: {integrity: sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
dev: false
/has-bigints/1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: true
@ -5521,6 +5530,10 @@ packages:
istanbul-lib-report: 3.0.0
dev: true
/iterall/1.3.0:
resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==}
dev: false
/jest-changed-files/28.0.2:
resolution: {integrity: sha512-QX9u+5I2s54ZnGoMEjiM2WeBvJR2J7w/8ZUmH2um/WLAuGAYFQcsVXY9+1YL6k0H/AGUdH8pXUAv6erDqEsvIA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -6212,6 +6225,10 @@ packages:
p-locate: 4.1.0
dev: true
/lodash.get/4.4.2:
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
dev: false
/lodash.includes/4.3.0:
resolution: {integrity: sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=}
dev: false
@ -7031,7 +7048,7 @@ packages:
requiresBuild: true
dependencies:
chokidar: 3.5.3
debug: 3.2.7_supports-color@5.5.0
debug: 3.2.7
ignore-by-default: 1.0.1
minimatch: 3.1.2
pstree.remy: 1.1.8
@ -8153,8 +8170,6 @@ packages:
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
dev: false
/serve-static/1.15.0:
@ -8165,8 +8180,6 @@ packages:
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.18.0
transitivePeerDependencies:
- supports-color
dev: false
/set-blocking/2.0.0:
@ -8670,6 +8683,25 @@ packages:
engines: {node: '>=10'}
dev: true
/type-graphql/1.1.1_graphql@16.5.0:
resolution: {integrity: sha512-iOOWVn0ehCYMukmnXStbkRwFE9dcjt7/oDcBS1JyQZo9CbhlIll4lHHps54HMEk4A4c8bUPd+DjK8w1/ZrxB4A==}
engines: {node: '>= 10.3'}
requiresBuild: true
peerDependencies:
class-validator: '>=0.12.0'
graphql: ^15.3.0
dependencies:
'@types/glob': 7.2.0
'@types/node': 17.0.31
'@types/semver': 7.3.10
glob: 7.2.0
graphql: 16.5.0
graphql-query-complexity: 0.7.2_graphql@16.5.0
graphql-subscriptions: 1.2.1_graphql@16.5.0
semver: 7.3.7
tslib: 2.4.0
dev: false
/type-is/1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}