feat: working

This commit is contained in:
Eli Bosley
2024-10-18 13:27:32 -04:00
parent 0c79995107
commit 545ccf1938
14 changed files with 107 additions and 93 deletions
+1 -1
View File
@@ -1 +1 @@
18.19.1
v22.10.0
+1 -1
View File
@@ -1,7 +1,7 @@
###########################################################
# Development/Build Image
###########################################################
FROM node:18.19.1-bookworm-slim As development
FROM node:22-bookworm-slim As development
# Install build tools and dependencies
RUN apt-get update -y && apt-get install -y \
+1 -1
View File
@@ -1,5 +1,5 @@
[api]
version="3.11.0+04f7b636"
version="THIS_WILL_BE_REPLACED_WHEN_BUILT"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
+1 -1
View File
@@ -1,5 +1,5 @@
[api]
version="3.11.0+04f7b636"
version="THIS_WILL_BE_REPLACED_WHEN_BUILT"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
+1 -1
View File
@@ -43,7 +43,7 @@
"install:unraid": "./scripts/install-in-unraid.sh",
"start:plugin": "INTROSPECTION=true LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
"start:plugin-verbose": "LOG_CONTEXT=true LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
"start:tsx": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace DOTENV_CONFIG_PATH=./.env.staging tsx -r dotenv/config src/cli.ts start --debug",
"start:tsx": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace DOTENV_CONFIG_PATH=./.env.development tsx -r dotenv/config src/cli.ts start --debug",
"start:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs start --debug'",
"restart:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs restart --debug'",
"stop:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs stop --debug'",
+9 -4
View File
@@ -4,8 +4,13 @@ import { am } from 'am';
import { main } from '@app/cli/index';
import { internalLogger } from '@app/core/log';
void am(main, (error: unknown) => {
internalLogger.fatal((error as Error).message);
// Ensure process is exited
try {
await main();
} catch (error) {
console.log(error);
internalLogger.error({
message: 'Failed to start unraid-api',
error,
});
process.exit(1);
});
}
+1 -5
View File
@@ -13,14 +13,10 @@ import {
type getServersQuery,
type getCloudQuery,
} from '../../graphql/generated/api/operations';
import {
type ApolloQueryResult,
type ApolloClient,
type NormalizedCacheObject,
} from '@apollo/client/core/core.cjs';
import { MinigraphStatus } from '@app/graphql/generated/api/types';
import { API_VERSION } from '@app/environment';
import { loadStateFiles } from '@app/store/modules/emhttp';
import { ApolloClient, ApolloQueryResult, NormalizedCacheObject } from '@apollo/client/core';
type CloudQueryResult = NonNullable<
ApolloQueryResult<getCloudQuery>['data']['cloud']
+2 -5
View File
@@ -12,7 +12,6 @@ import { API_VERSION } from '@app/environment';
*/
export const start = async () => {
// Set process title
process.title = 'unraid-api';
const runningProcesses = await getAllUnraidApiPids();
if (runningProcesses.length > 0) {
@@ -24,8 +23,6 @@ export const start = async () => {
// Start API
cliLogger.info('Starting unraid-api@v%s', API_VERSION);
// If we're in debug mode or we're NOT
console.log(mainOptions);
// in debug but ARE in the child process
if (mainOptions.debug || process.env._DAEMONIZE_PROCESS) {
// Log when the API exits
@@ -52,10 +49,10 @@ export const start = async () => {
logToSyslog('✔️ UNRAID API started successfully!');
}
console.log(mainOptions);
await import ('@app/index.ts');
await import ('@app/index.ts');
if (!mainOptions.debug) {
console.log('Daemonizing process. %s %o', process.execPath, process.argv);
if ('_DAEMONIZE_PROCESS' in process.env) {
// In the child, clean up the tracking environment variable
delete process.env._DAEMONIZE_PROCESS;
@@ -0,0 +1,25 @@
import { gql, QueryOptions } from "@apollo/client/core";
interface ParsedQuery {
query?: string;
variables?: Record<string, string>;
}
export const parseGraphQLQuery = (body: string): QueryOptions => {
try {
const parsedBody: ParsedQuery = JSON.parse(body);
if (
parsedBody.query &&
parsedBody.variables &&
typeof parsedBody.variables === 'object'
) {
return {
query: gql(parsedBody.query),
variables: parsedBody.variables,
};
}
throw new Error('Invalid Body');
} catch (error) {
throw new Error('Invalid Body Provided');
}
};
+2 -2
View File
@@ -2,8 +2,8 @@ import { join } from 'path';
import { loadFilesSync } from '@graphql-tools/load-files';
import { mergeTypeDefs } from '@graphql-tools/merge';
const files = loadFilesSync(join(import.meta.dirname, '../src/graphql/schema/types'), {
const files = loadFilesSync(join(import.meta.dirname, './types'), {
extensions: ['graphql'],
});
export const typeDefs = mergeTypeDefs(files);
export const typeDefs = mergeTypeDefs(files);
+58 -62
View File
@@ -1,7 +1,6 @@
import 'reflect-metadata';
import 'global-agent/bootstrap';
import { am } from 'am';
import http from 'http';
import https from 'https';
import CacheableLookup from 'cacheable-lookup';
@@ -31,90 +30,87 @@ import { setupLogRotation } from '@app/core/logrotate/setup-logrotate';
import { WebSocket } from 'ws';
import * as env from '@app/environment';
let server: NestFastifyApplication<RawServerDefault>;
let server: NestFastifyApplication<RawServerDefault> | null = null;
const unlinkUnixPort = () => {
if (isNaN(parseInt(PORT, 10))) {
if (fileExistsSync(PORT)) unlinkSync(PORT);
}
};
// Boot app
void am(
async () => {
environment.IS_MAIN_PROCESS = true;
try {
environment.IS_MAIN_PROCESS = true;
logger.debug('ENV %o', env);
logger.debug('ENV %o', env);
const cacheable = new CacheableLookup();
const cacheable = new CacheableLookup();
Object.assign(global, { WebSocket });
// Ensure all DNS lookups are cached for their TTL
cacheable.install(http.globalAgent);
cacheable.install(https.globalAgent);
Object.assign(global, { WebSocket });
// Ensure all DNS lookups are cached for their TTL
cacheable.install(http.globalAgent);
cacheable.install(https.globalAgent);
// Start file <-> store sync
// Must occur before config is loaded to ensure that the handler can fix broken configs
await startStoreSync();
// Start file <-> store sync
// Must occur before config is loaded to ensure that the handler can fix broken configs
await startStoreSync();
await setupLogRotation();
await setupLogRotation();
// Load my servers config file into store
await store.dispatch(loadConfigFile());
// Load my servers config file into store
await store.dispatch(loadConfigFile());
// Load emhttp state into store
await store.dispatch(loadStateFiles());
// Load emhttp state into store
await store.dispatch(loadStateFiles());
// Load initial registration key into store
await store.dispatch(loadRegistrationKey());
// Load initial registration key into store
await store.dispatch(loadRegistrationKey());
// Load my dynamix config file into store
await store.dispatch(loadDynamixConfigFile());
// Load my dynamix config file into store
await store.dispatch(loadDynamixConfigFile());
// Start listening to file updates
StateManager.getInstance();
// Start listening to file updates
StateManager.getInstance();
// Start listening to key file changes
setupRegistrationKeyWatch();
// Start listening to key file changes
setupRegistrationKeyWatch();
// Start listening to docker events
setupVarRunWatch();
// Start listening to docker events
setupVarRunWatch();
// Start listening to dynamix config file changes
setupDynamixConfigWatch();
// Start listening to dynamix config file changes
setupDynamixConfigWatch();
// Disabled until we need the access token to work
// TokenRefresh.init();
// Disabled until we need the access token to work
// TokenRefresh.init();
// If port is unix socket, delete old socket before starting http server
unlinkUnixPort();
// If port is unix socket, delete old socket before starting http server
// Start webserver
server = await bootstrapNestServer();
PingTimeoutJobs.init();
startMiddlewareListeners();
await validateApiKeyIfPresent();
// On process exit stop HTTP server - this says it supports async but it doesnt seem to
exitHook(() => {
server?.close?.();
// If port is unix socket, delete socket before exiting
unlinkUnixPort();
// Start webserver
server = await bootstrapNestServer();
PingTimeoutJobs.init();
startMiddlewareListeners();
await validateApiKeyIfPresent();
// On process exit stop HTTP server - this says it supports async but it doesnt seem to
exitHook(() => {
server?.close?.();
// If port is unix socket, delete socket before exiting
unlinkUnixPort();
shutdownApiEvent();
process.exit(0);
});
},
async (error: NodeJS.ErrnoException) => {
logger.error('API-GLOBAL-ERROR %s %s', error.message, error.stack);
if (server) {
await server?.close?.();
}
shutdownApiEvent();
// Kill application
process.exit(1);
process.exit(0);
});
} catch (error: unknown) {
if (error instanceof Error) {
logger.error('API-ERROR %s %s', error.message, error.stack);
}
);
if (server) {
await server?.close?.();
}
shutdownApiEvent();
// Kill application
process.exit(1);
}
+1 -1
View File
@@ -11,7 +11,7 @@ import {
ApolloClient,
InMemoryCache,
type NormalizedCacheObject,
} from '@apollo/client/core/core.cjs';
} from '@apollo/client/core';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { MinigraphStatus } from '@app/graphql/generated/api/types';
import { API_VERSION } from '@app/environment';
+2 -1
View File
@@ -38,6 +38,7 @@
], /* List of folders to include type definitions from. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"resolveJsonModule": false
"resolveJsonModule": false,
"allowImportingTsExtensions": true
}
}
+2 -8
View File
@@ -1,9 +1,3 @@
import type {
split as SplitType,
ApolloClient as ApolloClientType,
NormalizedCacheObject,
} from "@apollo/client";
import {
from,
ApolloClient,
@@ -85,7 +79,7 @@ const retryLink = new RetryLink({
},
});
const splitLinks = (split as typeof SplitType)(
const splitLinks = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
@@ -103,7 +97,7 @@ const splitLinks = (split as typeof SplitType)(
*/
const additiveLink = from([errorLink, retryLink, splitLinks]);
const client: ApolloClientType<NormalizedCacheObject> = new ApolloClient({
const client = new ApolloClient({
link: additiveLink,
cache: createApolloCache(),
});