Refactors loading function
* Initialize service component and handler in hooks * Dynamically watch the config instead of a reload on each page * Uses derived store for public server config * Moves service data in its own page data property * Reverts to a array to store group/services
This commit is contained in:
@@ -2,21 +2,20 @@ title: 'Hello World !!'
|
|||||||
subtitle: 'actually, I am a new pilot.'
|
subtitle: 'actually, I am a new pilot.'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
cloud:
|
- title: '/Cloud'
|
||||||
title: '/Cloud'
|
|
||||||
subtitle: 'Private Cloud Utilities'
|
subtitle: 'Private Cloud Utilities'
|
||||||
icon: 'fas fa-cloud'
|
icon: 'fas fa-cloud'
|
||||||
items:
|
items:
|
||||||
nas:
|
- title: 'NAS'
|
||||||
title: 'NAS'
|
|
||||||
subtitle: 'Network Attached Storage'
|
subtitle: 'Network Attached Storage'
|
||||||
icon: 'fas fa-hard-drive'
|
icon: 'fas fa-hard-drive'
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
url: '/NAS'
|
url: '/NAS'
|
||||||
keywords: 'cloud storage files'
|
|
||||||
type: prowlarr
|
type: prowlarr
|
||||||
pihole:
|
|
||||||
title: 'PiHole'
|
keywords: 'cloud storage files'
|
||||||
|
|
||||||
|
- title: 'PiHole'
|
||||||
subtitle: 'A DNS Hole'
|
subtitle: 'A DNS Hole'
|
||||||
icon: 'fas fa-hard-drive'
|
icon: 'fas fa-hard-drive'
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
|
|||||||
3
src/hooks.client.ts
Normal file
3
src/hooks.client.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { initServices } from '$lib/services/services';
|
||||||
|
|
||||||
|
await initServices();
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
import { dev } from '$app/environment';
|
||||||
|
import { watchDymamicConfig } from '$lib/server/config';
|
||||||
import { initServices } from '$lib/services/services';
|
import { initServices } from '$lib/services/services';
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
|
watchDymamicConfig();
|
||||||
|
}
|
||||||
|
|
||||||
await initServices();
|
await initServices();
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import configData from '../config.yml';
|
|
||||||
|
|
||||||
export interface BrandConfig {
|
|
||||||
logo?: string;
|
|
||||||
icon?: string;
|
|
||||||
usemask?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SectionConfig extends BrandConfig {
|
|
||||||
title: string;
|
|
||||||
subtitle?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServiceConfig extends SectionConfig {
|
|
||||||
url: string;
|
|
||||||
target?: string;
|
|
||||||
type?: string;
|
|
||||||
data?: Record<string, unknown>;
|
|
||||||
|
|
||||||
[x: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServiceGroupConfig extends SectionConfig {
|
|
||||||
items: Record<string, ServiceConfig>;
|
|
||||||
|
|
||||||
[x: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Config extends SectionConfig {
|
|
||||||
services: Record<string, ServiceGroupConfig>;
|
|
||||||
|
|
||||||
[x: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeepRequired<T> = T extends object
|
|
||||||
? {
|
|
||||||
[P in keyof T]: DeepRequired<T[P]>;
|
|
||||||
}
|
|
||||||
: T;
|
|
||||||
|
|
||||||
const requiredConfig: DeepRequired<Config> = {
|
|
||||||
title: '',
|
|
||||||
subtitle: '',
|
|
||||||
services: {
|
|
||||||
default: {
|
|
||||||
title: '',
|
|
||||||
items: {
|
|
||||||
default: {
|
|
||||||
title: '',
|
|
||||||
url: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function mergeConfig(a: Config, b: any): Config {
|
|
||||||
return { ...a, ...b };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultConfig: Config = {
|
|
||||||
title: 'Flanders',
|
|
||||||
|
|
||||||
services: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
type SPOJO = Record<string, unknown>;
|
|
||||||
|
|
||||||
function strip<Type extends SPOJO>(toStrip: Type, reference: Type): Type {
|
|
||||||
const res: Type = { ...toStrip };
|
|
||||||
const referenceNames = Object.entries(reference).map(([key, value]) => key);
|
|
||||||
|
|
||||||
const allowAny: boolean = referenceNames.length == 1 && referenceNames[0] == 'default';
|
|
||||||
for (const [key, value] of Object.entries(res)) {
|
|
||||||
if (referenceNames.includes(key) == false && allowAny == false) {
|
|
||||||
// remove the object
|
|
||||||
delete res[key];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (typeof value != 'object') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is a child object, we strip it further
|
|
||||||
const stripped: SPOJO = {};
|
|
||||||
const childReference: SPOJO = reference[allowAny ? 'default' : key] as SPOJO;
|
|
||||||
stripped[key] = strip(value as SPOJO, childReference);
|
|
||||||
Object.assign(res, stripped);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stripPrivateFields(config: Config): Config {
|
|
||||||
return strip(config, requiredConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config: Config = mergeConfig(defaultConfig, configData);
|
|
||||||
|
|
||||||
export const clientConfig: Config = stripPrivateFields(config);
|
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { config, defaultConfig, mergeConfig, type Config, stripPrivateFields } from './config';
|
import {
|
||||||
|
defaultConfig,
|
||||||
|
mergeConfig,
|
||||||
|
type Config,
|
||||||
|
stripPrivateFields,
|
||||||
|
serverConfig,
|
||||||
|
clientConfig
|
||||||
|
} from './config';
|
||||||
|
|
||||||
describe('Config', () => {
|
describe('Config', () => {
|
||||||
it('should be export a build time config', () => {
|
it('should be export a build time server and client config store', () => {
|
||||||
expect(config).toBeTruthy();
|
expect(serverConfig).toBeTruthy();
|
||||||
|
expect(clientConfig).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to merge with POJO', () => {
|
it('should be able to merge with POJO', () => {
|
||||||
@@ -34,25 +42,33 @@ describe('Config', () => {
|
|||||||
const custom: Config = {
|
const custom: Config = {
|
||||||
title: 'custom',
|
title: 'custom',
|
||||||
secret: { secret: 'some secret' },
|
secret: { secret: 'some secret' },
|
||||||
services: {
|
services: [
|
||||||
top: {
|
{
|
||||||
title: 'top services',
|
title: 'top services',
|
||||||
secret: 'secret',
|
secret: 'secret',
|
||||||
items: {
|
items: [
|
||||||
top: {
|
{
|
||||||
title: 'top service',
|
title: 'top service',
|
||||||
url: 'somewhere',
|
url: 'somewhere',
|
||||||
secret: 'secret'
|
secret: 'secret',
|
||||||
}
|
type: 'foo'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const stripped: Config = stripPrivateFields(custom);
|
const stripped: Config = stripPrivateFields(custom);
|
||||||
|
|
||||||
expect(stripped.secret).toBeUndefined();
|
expect(stripped.secret).toBeUndefined();
|
||||||
expect(stripped.services.top.secret).toBeUndefined();
|
expect(custom.secret).toEqual({ secret: 'some secret' });
|
||||||
expect(stripped.services.top.items.top.secret).toBeUndefined();
|
expect(stripped.services[0].secret).toBeUndefined();
|
||||||
|
expect(custom.services[0].secret).toEqual('secret');
|
||||||
|
expect(stripped.services[0].title).toEqual('top services');
|
||||||
|
expect(stripped.services[0].items[0].secret).toBeUndefined();
|
||||||
|
expect(custom.services[0].items[0].secret).toEqual('secret');
|
||||||
|
expect(stripped.services[0].items[0].title).toEqual('top service');
|
||||||
|
expect(stripped.services[0].items[0].url).toEqual('somewhere');
|
||||||
|
expect(stripped.services[0].items[0].type).toEqual('foo');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
146
src/lib/server/config.ts
Normal file
146
src/lib/server/config.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import { writable, type Readable, type Writable, derived } from 'svelte/store';
|
||||||
|
import configData from '../../config.yml';
|
||||||
|
import { readFile, watch } from 'fs/promises';
|
||||||
|
import * as yml from 'js-yaml';
|
||||||
|
|
||||||
|
export interface BrandConfig {
|
||||||
|
logo?: string;
|
||||||
|
icon?: string;
|
||||||
|
usemask?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SectionConfig extends BrandConfig {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceConfig extends SectionConfig {
|
||||||
|
url: string;
|
||||||
|
target?: string;
|
||||||
|
type?: string;
|
||||||
|
|
||||||
|
[x: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceGroupConfig extends SectionConfig {
|
||||||
|
items: Array<ServiceConfig>;
|
||||||
|
|
||||||
|
[x: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config extends SectionConfig {
|
||||||
|
services: Array<ServiceGroupConfig>;
|
||||||
|
|
||||||
|
[x: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredService: Required<ServiceConfig> = {
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
logo: '',
|
||||||
|
icon: '',
|
||||||
|
usemask: false,
|
||||||
|
url: '',
|
||||||
|
target: '',
|
||||||
|
type: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const requiredServiceGroup: Required<ServiceGroupConfig> = {
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
logo: '',
|
||||||
|
icon: '',
|
||||||
|
usemask: false,
|
||||||
|
items: [requiredService]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const requiredConfig: Required<Config> = {
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
logo: '',
|
||||||
|
icon: '',
|
||||||
|
usemask: false,
|
||||||
|
services: [requiredServiceGroup]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function mergeConfig(a: Config, b: any): Config {
|
||||||
|
return { ...a, ...b };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultConfig: Config = {
|
||||||
|
title: 'Flanders',
|
||||||
|
|
||||||
|
services: []
|
||||||
|
};
|
||||||
|
|
||||||
|
type SPOJO = Record<string, unknown>;
|
||||||
|
|
||||||
|
function strip<Type extends SPOJO>(toStrip: Type, reference: Type): Type {
|
||||||
|
const res: Type = { ...toStrip };
|
||||||
|
const referenceNames = Object.entries(reference).map(([key, value]) => key);
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(res)) {
|
||||||
|
if (referenceNames.includes(key) == false) {
|
||||||
|
// remove the object
|
||||||
|
delete res[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (typeof value != 'object') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Array) {
|
||||||
|
const newValue = [];
|
||||||
|
const childReference: SPOJO = (reference[key] as Array<SPOJO>)[0];
|
||||||
|
for (const child of value) {
|
||||||
|
newValue.push(strip(child, childReference));
|
||||||
|
}
|
||||||
|
(res as SPOJO)[key] = newValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is a child object, we strip it further
|
||||||
|
const stripped: SPOJO = {};
|
||||||
|
const childReference: SPOJO = (reference[key] as Array<SPOJO>)[0];
|
||||||
|
stripped[key] = strip(value as SPOJO, childReference);
|
||||||
|
(res as SPOJO)[key] = stripped;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stripPrivateFields(config: Config): Config {
|
||||||
|
return strip(config, requiredConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const serverConfig: Writable<Config> = writable(mergeConfig(defaultConfig, configData));
|
||||||
|
|
||||||
|
export const clientConfig: Readable<Config> = derived(serverConfig, ($config) =>
|
||||||
|
stripPrivateFields($config)
|
||||||
|
);
|
||||||
|
|
||||||
|
export function watchDymamicConfig() {
|
||||||
|
const __filepath = '/dynamic/config.yml';
|
||||||
|
|
||||||
|
const reloadConfig = async () => {
|
||||||
|
try {
|
||||||
|
const dynamicConfig = yml.load(await readFile(__filepath, 'utf8'));
|
||||||
|
serverConfig.set(mergeConfig(defaultConfig, dynamicConfig));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('could not read or parse config: ' + err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reloadConfig();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const watcher = watch(__filepath);
|
||||||
|
for await (const event of watcher) {
|
||||||
|
reloadConfig();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('could not watch config: ' + err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { type ServiceHandler } from '../service';
|
import { type ServiceComponentPath, type ServiceHandler } from '../service';
|
||||||
|
|
||||||
export const handle: ServiceHandler = ({ config }) => {
|
export const handle: ServiceHandler = () => {
|
||||||
const data = {};
|
return { piholedata: 'coucou' };
|
||||||
if (config.apikey != undefined) {
|
|
||||||
//TODO: fetch data
|
|
||||||
}
|
|
||||||
return { data, componentPath: 'pihole/PiHoleContent.svelte' };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const path: ServiceComponentPath = 'pihole/PiHoleContent.svelte';
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { ServiceHandler } from '../service';
|
import type { ServiceComponentPath, ServiceHandler } from '../service';
|
||||||
|
|
||||||
export const handle: ServiceHandler = () => {
|
export const handle: ServiceHandler = () => {
|
||||||
return { data: {}, componentPath: 'prowlarr/ProwlarrContent.svelte' };
|
return { prowlardata: 'coucou' };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const path: ServiceComponentPath = 'prowlarr/ProwlarrContent.svelte';
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ interface ServiceHandlerArgs {
|
|||||||
config: ServiceConfig;
|
config: ServiceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ServiceHandler = (input: ServiceHandlerArgs) => {
|
export type ServiceHandler = (input: ServiceHandlerArgs) => Record<string, unknown>;
|
||||||
data: Record<string, unknown>;
|
|
||||||
componentPath: string;
|
export type ServiceComponentPath = string;
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,33 +1,43 @@
|
|||||||
import type { ServiceHandler } from './service';
|
import type { ServiceHandler } from './service';
|
||||||
|
|
||||||
const services: Record<string, [ServiceHandler, string]> = {};
|
type ServiceRecord = {
|
||||||
|
handler: ServiceHandler;
|
||||||
|
component: any;
|
||||||
|
};
|
||||||
|
|
||||||
function registerService(type: string, handler: ServiceHandler) {
|
const services: Record<string, ServiceRecord> = {};
|
||||||
services[type] = [handler, type];
|
|
||||||
|
function registerService(type: string, handler: ServiceHandler, component: any) {
|
||||||
|
services[type] = { handler, component };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getService(type: string): [ServiceHandler, string] {
|
export function getServiceHandler(type: string): ServiceHandler | undefined {
|
||||||
const handler = services[type];
|
return services[type]?.handler;
|
||||||
if (handler == undefined) {
|
}
|
||||||
return [
|
|
||||||
() => {
|
export function getServiceComponent(type: string): any {
|
||||||
return { data: {}, componentPath: '' };
|
return services[type]?.component;
|
||||||
},
|
|
||||||
''
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initServices() {
|
export async function initServices() {
|
||||||
const services = import.meta.glob('/src/lib/services/**/+service.ts');
|
const services = import.meta.glob('/src/lib/services/**/+service.ts');
|
||||||
|
|
||||||
for (const [path, load] of Object.entries(services)) {
|
for (const [modulePath, load] of Object.entries(services)) {
|
||||||
const { handle } = (await load()) as any;
|
try {
|
||||||
|
const { handle, path } = (await load()) as any;
|
||||||
if (handle == undefined) {
|
if (handle == undefined) {
|
||||||
continue;
|
throw new Error(`${modulePath} does not export 'handle'`);
|
||||||
|
}
|
||||||
|
if (path == undefined) {
|
||||||
|
throw new Error(`${modulePath} does not export 'path'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = await import(/* @vite-ignore */ '/src/lib/services/' + path);
|
||||||
|
|
||||||
|
const typeName = modulePath.slice(18, -12);
|
||||||
|
registerService(typeName, handle, module.default);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Could not load service definition from '${modulePath}': ${err}`);
|
||||||
}
|
}
|
||||||
const typeName = path.slice(18, -12);
|
|
||||||
registerService(typeName, handle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,25 @@
|
|||||||
import { dev } from '$app/environment';
|
import { clientConfig, serverConfig } from '$lib/server/config';
|
||||||
import { config, stripPrivateFields, type Config, type ServiceConfig } from '$lib/config';
|
import { get } from 'svelte/store';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
import * as yml from 'js-yaml';
|
import { getServiceHandler } from '$lib/services/services';
|
||||||
import { readFile } from 'fs/promises';
|
|
||||||
import { getService } from '$lib/services/services';
|
|
||||||
|
|
||||||
async function reloadConfig(): Promise<Config> {
|
export const load: PageServerLoad = ({ fetch }) => {
|
||||||
if (dev) {
|
const config = get(clientConfig);
|
||||||
return config;
|
const serviceData: Array<Array<unknown>> = [];
|
||||||
}
|
|
||||||
try {
|
|
||||||
const dynamic = yml.load(await readFile('/dynamic/config.yml', 'utf8'));
|
|
||||||
return { ...config, ...dynamic };
|
|
||||||
} catch (err) {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ fetch, depends }) => {
|
for (const [i, group] of get(serverConfig).services.entries()) {
|
||||||
depends('app:state');
|
const groupData: Array<unknown> = [];
|
||||||
|
serviceData.push(groupData);
|
||||||
const serverConfig = await reloadConfig();
|
for (const [j, service] of group.items.entries()) {
|
||||||
const clientConfig = stripPrivateFields(serverConfig);
|
const handler = getServiceHandler(service.type || '');
|
||||||
const groups: Array<string> = Object.entries(serverConfig.services).map(([k, v]) => k);
|
if (handler == undefined) {
|
||||||
for (const group of groups) {
|
config.services[i].items[j].type = undefined;
|
||||||
const serverGroup = serverConfig.services[group];
|
groupData.push(undefined);
|
||||||
const clientGroup = clientConfig.services[group];
|
} else {
|
||||||
|
groupData.push(handler({ fetch, config: service }));
|
||||||
const services = Object.entries(serverGroup.items).map(([k, v]) => k);
|
|
||||||
|
|
||||||
for (const service of services) {
|
|
||||||
const serverService = serverGroup.items[service] as ServiceConfig;
|
|
||||||
const clientService = clientGroup.items[service] as ServiceConfig;
|
|
||||||
|
|
||||||
const [handler, type] = getService(serverService.type || '');
|
|
||||||
|
|
||||||
clientService.type = type;
|
|
||||||
Object.assign(clientService, handler({ fetch, config: serverService }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clientConfig.timestamp = new Date();
|
}
|
||||||
return { config: clientConfig, cool: true };
|
|
||||||
|
return { config, serviceData };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { invalidate } from '$app/navigation';
|
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
onMount(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
invalidate('app:state');
|
|
||||||
}, 10000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>{data.config.title}</h1>
|
<h1>{data.config.title}</h1>
|
||||||
@@ -17,4 +9,19 @@
|
|||||||
<h2>{data.config.subtitle}</h2>
|
<h2>{data.config.subtitle}</h2>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<pre> {JSON.stringify(data.config, null, 4)} </pre>
|
<ul>
|
||||||
|
{#each data.config.services as group, i}
|
||||||
|
<li>
|
||||||
|
<p>{group.title}</p>
|
||||||
|
<ul>
|
||||||
|
{#each group.items as service, j}
|
||||||
|
<li>
|
||||||
|
{i},{j}
|
||||||
|
<pre> {JSON.stringify(service)}</pre>
|
||||||
|
<pre> {JSON.stringify(data.serviceData[i][j])}</pre>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { PageLoad } from './$types';
|
|
||||||
|
|
||||||
export const load: PageLoad = async ({ data }) => {
|
|
||||||
const config = { ...data.config };
|
|
||||||
const groups: Array<string> = Object.entries(config.services).map(([k, v]) => k);
|
|
||||||
|
|
||||||
const components: Record<string, any> = {};
|
|
||||||
|
|
||||||
for (const group of groups) {
|
|
||||||
const services: Array<string> = Object.entries(config.services[group].items).map(
|
|
||||||
([k, v]) => k
|
|
||||||
);
|
|
||||||
for (const s of services) {
|
|
||||||
const service = data.config.services[group].items[s];
|
|
||||||
if (service.componentPath == '' || service.type == '') {
|
|
||||||
delete service.componentPath;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const componentType = service.type || '';
|
|
||||||
if (components[componentType] != undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = '../lib/services/' + service.componentPath;
|
|
||||||
|
|
||||||
const module = await import(/* @vite-ignore */ path);
|
|
||||||
|
|
||||||
components[componentType] = module.default;
|
|
||||||
|
|
||||||
//service.componentPath = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { config, components };
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user