Attempts to a broken dynamic service loading.
This commit is contained in:
@@ -5,5 +5,6 @@
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
|
||||
"tabWidth": 4
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ services:
|
||||
icon: 'fas fa-hard-drive'
|
||||
target: '_blank'
|
||||
url: '/NAS'
|
||||
type: 'hello'
|
||||
keywords: 'cloud storage files'
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { dev } from "$app/environment";
|
||||
import { clientConfig, stripPrivateFields, type Config } from "$lib/config";
|
||||
import type { Handle } from "@sveltejs/kit";
|
||||
import { readFile } from 'fs';
|
||||
import * as yml from 'js-yaml';
|
||||
|
||||
async function reloadConfig(): Promise<Config> {
|
||||
try {
|
||||
const dynamic = yml.load(await readFile('/dynamic/config.yml', 'utf8'));
|
||||
return stripPrivateFields({ ...clientConfig, ...dynamic });
|
||||
} catch (err) {
|
||||
return clientConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
if ( dev == false ) {
|
||||
Object.assign(event.locals,{config: await reloadConfig()});
|
||||
}
|
||||
|
||||
const response = await resolve(event);
|
||||
return response;
|
||||
}
|
||||
@@ -1,32 +1,33 @@
|
||||
import configData from '../config.yml';
|
||||
|
||||
export interface Brand {
|
||||
export interface BrandConfig {
|
||||
logo?: string;
|
||||
icon?: string;
|
||||
usemask?: boolean;
|
||||
}
|
||||
|
||||
export interface Section extends Brand {
|
||||
export interface SectionConfig extends BrandConfig {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
export interface Service extends Section {
|
||||
export interface ServiceConfig extends SectionConfig {
|
||||
url: string;
|
||||
target?: string;
|
||||
type?: string;
|
||||
data?: Record<string, unknown>;
|
||||
|
||||
[x: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ServiceGroup extends Section {
|
||||
items: Service[];
|
||||
export interface ServiceGroupConfig extends SectionConfig {
|
||||
items: ServiceConfig[];
|
||||
|
||||
[x: string]: unknown;
|
||||
}
|
||||
|
||||
export interface Config extends Section {
|
||||
services: ServiceGroup[];
|
||||
export interface Config extends SectionConfig {
|
||||
services: ServiceGroupConfig[];
|
||||
|
||||
[x: string]: unknown;
|
||||
}
|
||||
@@ -46,9 +47,9 @@ const requiredConfig: DeepRequired<Config> = {
|
||||
items: [
|
||||
{
|
||||
title: '',
|
||||
url: '',
|
||||
url: ''
|
||||
}
|
||||
],
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -63,47 +64,44 @@ export const defaultConfig: Config = {
|
||||
services: []
|
||||
};
|
||||
|
||||
type SPOJO = Record<string, unknown>;
|
||||
|
||||
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);
|
||||
|
||||
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 ) {
|
||||
for (const [key, value] of Object.entries(res)) {
|
||||
if (referenceNames.includes(key) == false) {
|
||||
// remove the object
|
||||
delete res[key];
|
||||
continue
|
||||
}
|
||||
|
||||
// strips further arrays
|
||||
if ( value instanceof Array ) {
|
||||
const stripped : SPOJO = {};
|
||||
const childRef = (reference[key] as Array<SPOJO>)[0];
|
||||
stripped[key] = value.map((v: SPOJO) => strip(v,childRef));
|
||||
Object.assign(res,stripped);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( typeof value != "object") {
|
||||
continue
|
||||
// strips further arrays
|
||||
if (value instanceof Array) {
|
||||
const stripped: SPOJO = {};
|
||||
const childRef = (reference[key] as Array<SPOJO>)[0];
|
||||
stripped[key] = value.map((v: SPOJO) => strip(v, childRef));
|
||||
Object.assign(res, stripped);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value != 'object') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// it is a child object, we strip it further
|
||||
const stripped : SPOJO = {};
|
||||
stripped[key] = strip(value as SPOJO,reference[key] as SPOJO);
|
||||
Object.assign(res,stripped);
|
||||
|
||||
const stripped: SPOJO = {};
|
||||
stripped[key] = strip(value as SPOJO, reference[key] as SPOJO);
|
||||
Object.assign(res, stripped);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function stripPrivateFields(config: Config): Config {
|
||||
return strip(config,requiredConfig);
|
||||
return strip(config, requiredConfig);
|
||||
}
|
||||
|
||||
|
||||
export const config: Config = mergeConfig(defaultConfig, configData);
|
||||
|
||||
export const clientConfig :Config = stripPrivateFields(config)
|
||||
export const clientConfig: Config = stripPrivateFields(config);
|
||||
|
||||
9
src/lib/services/generic/GenericServiceCard.svelte
Normal file
9
src/lib/services/generic/GenericServiceCard.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { ServiceConfig } from '$lib/config';
|
||||
|
||||
export let data: ServiceConfig;
|
||||
</script>
|
||||
|
||||
<div class="service-card">
|
||||
<title>{data.title}</title>
|
||||
</div>
|
||||
27
src/lib/services/service.ts
Normal file
27
src/lib/services/service.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { ServiceConfig } from '$lib/config';
|
||||
|
||||
interface ServiceHandlerArgs {
|
||||
fetch: typeof fetch;
|
||||
config: ServiceConfig;
|
||||
}
|
||||
|
||||
export type ServiceHandler = (input: Partial<ServiceHandlerArgs>) => {
|
||||
data: Record<string, unknown>;
|
||||
componentPath: string;
|
||||
};
|
||||
|
||||
const services: Record<string, ServiceHandler> = {};
|
||||
|
||||
export function registerService(type: string, handler: ServiceHandler) {
|
||||
services[type] = handler;
|
||||
}
|
||||
|
||||
export function getService(type: string): ServiceHandler {
|
||||
const handler = services[type];
|
||||
if (handler == undefined) {
|
||||
return () => {
|
||||
return { data: {}, componentPath: 'generic/GenericServiceCard.svelte' };
|
||||
};
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
@@ -1,12 +1,39 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { clientConfig, type Config } from '$lib/config';
|
||||
import { config, stripPrivateFields, type Config } from '$lib/config';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import * as yml from 'js-yaml';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { getService } from '$lib/services/service';
|
||||
|
||||
|
||||
export const load: PageServerLoad = ({locals}) => {
|
||||
async function reloadConfig(): Promise<Config> {
|
||||
if (dev) {
|
||||
return { config: clientConfig };
|
||||
return config;
|
||||
}
|
||||
try {
|
||||
const dynamic = yml.load(await readFile('/dynamic/config.yml', 'utf8'));
|
||||
return { ...config, ...dynamic };
|
||||
} catch (err) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
return {config: (locals as Record<string,Config>).config || clientConfig};
|
||||
function zip<T>(a: Array<T>, b: Array<T>): Array<Array<T>> {
|
||||
const size = Math.min(a.length, b.length);
|
||||
return a.slice(0, size).map((v, i) => [v, b[i]]);
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ fetch, depends }) => {
|
||||
depends('app:state');
|
||||
|
||||
const serverConfig = await reloadConfig();
|
||||
const clientConfig = stripPrivateFields(serverConfig);
|
||||
|
||||
for (const [serverGroup, clientGroup] of zip(serverConfig.services, clientConfig.services)) {
|
||||
for (const [serverService, clientService] of zip(serverGroup.items, clientGroup.items)) {
|
||||
const handler = getService(serverService.type || '');
|
||||
Object.assign(clientService, handler({ fetch, config: serverService }));
|
||||
}
|
||||
}
|
||||
clientConfig.timestamp = new Date();
|
||||
return { config: clientConfig };
|
||||
};
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { invalidate } from '$app/navigation';
|
||||
|
||||
export let data: PageData;
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
invalidate('app:state');
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1>{data.config.title}</h1>
|
||||
{#if data.config.subtitle}
|
||||
<h2>{data.config.subtitle}</h2>
|
||||
{/if}
|
||||
|
||||
<pre> {JSON.stringify(data.config, null, 4)} </pre>
|
||||
|
||||
15
src/routes/+page.ts
Normal file
15
src/routes/+page.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ data }) => {
|
||||
for (const group of data.config.services) {
|
||||
for (const service of group.items) {
|
||||
const path =
|
||||
'../lib/services/' + (service.componentPath || 'generic/GenericServiceCard.svelte');
|
||||
const module = await import(/* @vite-ignore */ path);
|
||||
|
||||
service.component = module.default;
|
||||
delete service.componentPath;
|
||||
}
|
||||
}
|
||||
return { config: data.config };
|
||||
};
|
||||
Reference in New Issue
Block a user