mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat: async hypervisor and FIXED vm listing
This commit is contained in:
@@ -34,6 +34,7 @@ if [ ! -d "$source_directory" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Change ownership on copy
|
||||
# Replace the value inside the rsync command with the user's input
|
||||
rsync_command="rsync -avz -e ssh $source_directory root@${server_name}:/usr/local/unraid-api"
|
||||
|
||||
@@ -44,14 +45,11 @@ echo "$rsync_command"
|
||||
eval "$rsync_command"
|
||||
exit_code=$?
|
||||
|
||||
# Run unraid-api restart on remote host
|
||||
dev=${DEV:-true}
|
||||
# Chown the directory
|
||||
ssh root@"${server_name}" "chown -R root:root /usr/local/unraid-api"
|
||||
|
||||
if [ "$dev" = true ]; then
|
||||
ssh root@"${server_name}" "INTROSPECTION=true unraid-api restart"
|
||||
else
|
||||
ssh root@"${server_name}" "unraid-api restart"
|
||||
fi
|
||||
# Run unraid-api restart on remote host
|
||||
ssh root@"${server_name}" "INTROSPECTION=true LOG_LEVEL=trace unraid-api restart"
|
||||
|
||||
# Play built-in sound based on the operating system
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
|
||||
@@ -5,25 +5,36 @@ import '@app/dotenv';
|
||||
import { execa } from 'execa';
|
||||
import { CommandFactory } from 'nest-commander';
|
||||
|
||||
import { cliLogger, internalLogger } from '@app/core/log';
|
||||
import { internalLogger, logger } from '@app/core/log';
|
||||
import { LOG_LEVEL } from '@app/environment';
|
||||
import { CliModule } from '@app/unraid-api/cli/cli.module';
|
||||
import { LogService } from '@app/unraid-api/cli/log.service';
|
||||
|
||||
const getUnraidApiLocation = async () => {
|
||||
try {
|
||||
const shellToUse = await execa('which unraid-api');
|
||||
if (shellToUse.code !== 0) {
|
||||
throw new Error('unraid-api not found');
|
||||
}
|
||||
return shellToUse.stdout.trim();
|
||||
} finally {
|
||||
return '/usr/bin/unraid-api';
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const shellToUse = await execa('which unraid-api')
|
||||
.then((res) => res.toString().trim())
|
||||
.catch((_) => '/usr/local/bin/unraid-api');
|
||||
await CommandFactory.run(CliModule, {
|
||||
cliName: 'unraid-api',
|
||||
logger: false, // new LogService(), - enable this to see nest initialization issues
|
||||
logger: LOG_LEVEL === 'TRACE' && new LogService(), // - enable this to see nest initialization issues
|
||||
completion: {
|
||||
fig: true,
|
||||
fig: false,
|
||||
cmd: 'completion-script',
|
||||
nativeShell: { executablePath: shellToUse },
|
||||
nativeShell: { executablePath: await getUnraidApiLocation() },
|
||||
},
|
||||
});
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
cliLogger.error('ERROR:', error);
|
||||
logger.error('ERROR:', error);
|
||||
internalLogger.error({
|
||||
message: 'Failed to start unraid-api',
|
||||
error,
|
||||
|
||||
@@ -18,9 +18,10 @@ const states = {
|
||||
* Get vm domains.
|
||||
*/
|
||||
export const getDomains = async () => {
|
||||
const { ConnectListAllDomainsFlags } = await import('@vmngr/libvirt');
|
||||
const { UnraidHypervisor } = await import('@app/core/utils/vms/get-hypervisor');
|
||||
try {
|
||||
const { ConnectListAllDomainsFlags } = await import('@vmngr/libvirt');
|
||||
const { UnraidHypervisor } = await import('@app/core/utils/vms/get-hypervisor');
|
||||
|
||||
const hypervisor = await UnraidHypervisor.getInstance().getHypervisor();
|
||||
if (!hypervisor) {
|
||||
throw new GraphQLError('VMs Disabled');
|
||||
|
||||
@@ -19,7 +19,7 @@ export const BYPASS_PERMISSION_CHECKS = process.env.BYPASS_PERMISSION_CHECKS ===
|
||||
export const BYPASS_CORS_CHECKS = process.env.BYPASS_CORS_CHECKS === 'true';
|
||||
export const LOG_CORS = process.env.LOG_CORS === 'true';
|
||||
export const LOG_TYPE = (process.env.LOG_TYPE as 'pretty' | 'raw') ?? 'pretty';
|
||||
export const LOG_LEVEL = process.env.LOG_LEVEL as
|
||||
export const LOG_LEVEL = process.env.LOG_LEVEL?.toUpperCase() as
|
||||
| 'TRACE'
|
||||
| 'DEBUG'
|
||||
| 'INFO'
|
||||
|
||||
34
api/src/graphql/schema/types/vms/domain.graphql
Normal file
34
api/src/graphql/schema/types/vms/domain.graphql
Normal file
@@ -0,0 +1,34 @@
|
||||
type Query {
|
||||
"""Virtual machines"""
|
||||
vms: Vms
|
||||
}
|
||||
|
||||
type Vms {
|
||||
id: ID!
|
||||
domain: [VmDomain!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
vms: Vms
|
||||
}
|
||||
|
||||
# https://libvirt.org/manpages/virsh.html#list
|
||||
enum VmState {
|
||||
NOSTATE
|
||||
RUNNING
|
||||
IDLE
|
||||
PAUSED
|
||||
SHUTDOWN
|
||||
SHUTOFF
|
||||
CRASHED
|
||||
PMSUSPENDED
|
||||
}
|
||||
|
||||
"""A virtual machine"""
|
||||
type VmDomain {
|
||||
uuid: ID!
|
||||
"""A friendly name for the vm"""
|
||||
name: String
|
||||
"""Current domain vm state"""
|
||||
state: VmState!
|
||||
}
|
||||
@@ -12,11 +12,13 @@ export class RestartCommand extends CommandRunner {
|
||||
|
||||
async run(_): Promise<void> {
|
||||
try {
|
||||
this.logger.info('Restarting the Unraid API');
|
||||
const { stderr, stdout } = await execa(PM2_PATH, [
|
||||
'restart',
|
||||
ECOSYSTEM_PATH,
|
||||
'--update-env',
|
||||
]);
|
||||
this.logger.info('Unraid API restarted');
|
||||
if (stderr) {
|
||||
this.logger.error(stderr);
|
||||
process.exit(1);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { ECOSYSTEM_PATH, PM2_PATH } from '@app/consts';
|
||||
import { levels, type LogLevel } from '@app/core/log';
|
||||
import type { LogService } from '@app/unraid-api/cli/log.service';
|
||||
import { LogService } from '@app/unraid-api/cli/log.service';
|
||||
|
||||
interface StartCommandOptions {
|
||||
'log-level'?: string;
|
||||
|
||||
@@ -58,7 +58,7 @@ const states = {
|
||||
},
|
||||
};
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Display')
|
||||
export class DisplayResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
||||
import { Resource } from '@app/graphql/generated/api/types';
|
||||
import { getters } from '@app/store/index';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Flash')
|
||||
export class FlashResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { UserAccount } from '@app/graphql/generated/api/types';
|
||||
import { Me, Resource } from '@app/graphql/generated/api/types';
|
||||
import { GraphqlUser } from '@app/unraid-api/auth/user.decorator';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Me')
|
||||
export class MeResolver {
|
||||
constructor() {}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
||||
|
||||
import { Resource } from '@app/graphql/generated/api/types';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Online')
|
||||
export class OnlineResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub';
|
||||
import { Resource } from '@app/graphql/generated/api/types';
|
||||
import { getters } from '@app/store/index';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Owner')
|
||||
export class OwnerResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -9,7 +9,7 @@ import { registrationType, Resource } from '@app/graphql/generated/api/types';
|
||||
import { getters } from '@app/store/index';
|
||||
import { FileLoadStatus } from '@app/store/types';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Registration')
|
||||
export class RegistrationResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Resource } from '@app/graphql/generated/api/types';
|
||||
import { type Server } from '@app/graphql/generated/client/graphql';
|
||||
import { getLocalServer } from '@app/graphql/schema/utils';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Server')
|
||||
export class ServerResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
||||
import { Resource } from '@app/graphql/generated/api/types';
|
||||
import { getters } from '@app/store/index';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Vars')
|
||||
export class VarsResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Query, Resolver } from '@nestjs/graphql';
|
||||
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
||||
|
||||
import { getDomains } from '@app/core/modules/vms/get-domains';
|
||||
import { Resource } from '@app/graphql/generated/api/types';
|
||||
import { Resource, type VmDomain } from '@app/graphql/generated/api/types';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Vms')
|
||||
export class VmsResolver {
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
@@ -14,17 +13,16 @@ export class VmsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async vms() {
|
||||
return {};
|
||||
console.log('Resolving Domains');
|
||||
return {
|
||||
id: 'vms',
|
||||
};
|
||||
}
|
||||
|
||||
@Resolver('domain')
|
||||
@Query()
|
||||
@UsePermissions({
|
||||
action: AuthActionVerb.READ,
|
||||
resource: 'vms/domain',
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async domain() {
|
||||
return getDomains();
|
||||
@ResolveField('domain')
|
||||
public async domain(): Promise<Array<VmDomain>> {
|
||||
const { getDomains } = await import('@app/core/modules/vms/get-domains');
|
||||
const domains = await getDomains();
|
||||
return domains;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user