Allow passing hostname, fastd-key and MAC as query-parameters when creating new node

This commit is contained in:
baldo 2022-08-25 20:37:34 +02:00
parent fb5bf934ff
commit 1de5bc0604
3 changed files with 110 additions and 53 deletions

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useConfigStore } from "@/stores/config"; import { useConfigStore } from "@/stores/config";
import { useNodeStore } from "@/stores/node"; import { useNodeStore } from "@/stores/node";
import { computed, nextTick, ref } from "vue"; import { computed, nextTick, onMounted, ref } from "vue";
import CONSTRAINTS from "@/shared/validation/constraints"; import CONSTRAINTS from "@/shared/validation/constraints";
import ActionButton from "@/components/form/ActionButton.vue"; import ActionButton from "@/components/form/ActionButton.vue";
import type { import type {
@ -30,6 +30,14 @@ import { ApiError } from "@/utils/Api";
const configStore = useConfigStore(); const configStore = useConfigStore();
const nodeStore = useNodeStore(); const nodeStore = useNodeStore();
interface Props {
hostname?: Hostname;
fastdKey?: FastdKey;
mac?: MAC;
}
const props = defineProps<Props>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: "create", node: StoredNode): void; (e: "create", node: StoredNode): void;
}>(); }>();
@ -44,13 +52,25 @@ const CONFLICT_MESSAGES: Record<string, string> = {
const conflictErrorMessage = ref<string | undefined>(undefined); const conflictErrorMessage = ref<string | undefined>(undefined);
const hostname = ref("" as Hostname); const hostnameModel = ref("" as Hostname);
const fastdKey = ref("" as FastdKey); const fastdKeyModel = ref("" as FastdKey);
const mac = ref("" as MAC); const macModel = ref("" as MAC);
const coords = ref("" as Coordinates); const coordsModel = ref("" as Coordinates);
const nickname = ref("" as Nickname); const nicknameModel = ref("" as Nickname);
const email = ref("" as EmailAddress); const emailModel = ref("" as EmailAddress);
const monitoring = ref(false); const monitoringModel = ref(false);
onMounted(() => {
if (props.hostname) {
hostnameModel.value = props.hostname;
}
if (props.fastdKey) {
fastdKeyModel.value = props.fastdKey;
}
if (props.mac) {
macModel.value = props.mac;
}
});
async function onSubmit() { async function onSubmit() {
generalError.value = false; generalError.value = false;
@ -61,13 +81,13 @@ async function onSubmit() {
try { try {
const node = await nodeStore.create({ const node = await nodeStore.create({
hostname: hostname.value, hostname: hostnameModel.value,
key: fastdKey.value || undefined, key: fastdKeyModel.value || undefined,
mac: mac.value, mac: macModel.value,
coords: coords.value || undefined, coords: coordsModel.value || undefined,
nickname: nickname.value, nickname: nicknameModel.value,
email: email.value, email: emailModel.value,
monitoring: monitoring.value, monitoring: monitoringModel.value,
}); });
emit("create", node); emit("create", node);
} catch (error) { } catch (error) {
@ -114,7 +134,7 @@ async function onSubmit() {
Beim Anlegen des Knotens ist ein Fehler aufgetreten. Bitte Beim Anlegen des Knotens ist ein Fehler aufgetreten. Bitte
probiere es später nochmal. Sollte dieses Problem weiter probiere es später nochmal. Sollte dieses Problem weiter
bestehen, so wende dich bitte per E-Mail an bestehen, so wende dich bitte per E-Mail an
<a :href="`mailto:${email}`">{{ email }}</a <a :href="`mailto:${emailModel}`">{{ emailModel }}</a
>. >.
</ErrorCard> </ErrorCard>
@ -122,7 +142,7 @@ async function onSubmit() {
<h3>Knotendaten</h3> <h3>Knotendaten</h3>
<ValidationFormInput <ValidationFormInput
v-model="hostname" v-model="hostnameModel"
label="Knotenname" label="Knotenname"
placeholder="z. B. Lisas-Freifunk" placeholder="z. B. Lisas-Freifunk"
:constraint="CONSTRAINTS.node.hostname" :constraint="CONSTRAINTS.node.hostname"
@ -130,7 +150,7 @@ async function onSubmit() {
validation-error="Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten." validation-error="Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten."
/> />
<ValidationFormInput <ValidationFormInput
v-model="fastdKey" v-model="fastdKeyModel"
label="VPN-Schlüssel (bitte nur weglassen, wenn Du weisst, was Du tust)" label="VPN-Schlüssel (bitte nur weglassen, wenn Du weisst, was Du tust)"
placeholder="Dein 64-stelliger VPN-Schlüssel" placeholder="Dein 64-stelliger VPN-Schlüssel"
:constraint="CONSTRAINTS.node.key" :constraint="CONSTRAINTS.node.key"
@ -138,7 +158,7 @@ async function onSubmit() {
validation-error="Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten." validation-error="Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten."
/> />
<ValidationFormInput <ValidationFormInput
v-model="mac" v-model="macModel"
label="MAC-Adresse" label="MAC-Adresse"
placeholder="z. B. 12:34:56:78:9a:bc oder 123456789abc" placeholder="z. B. 12:34:56:78:9a:bc oder 123456789abc"
:constraint="CONSTRAINTS.node.mac" :constraint="CONSTRAINTS.node.mac"
@ -165,14 +185,14 @@ async function onSubmit() {
</p> </p>
<ValidationFormInput <ValidationFormInput
v-model="nickname" v-model="nicknameModel"
label="Nickname / Name" label="Nickname / Name"
placeholder="z. B. Lisa" placeholder="z. B. Lisa"
:constraint="CONSTRAINTS.node.nickname" :constraint="CONSTRAINTS.node.nickname"
validation-error="Nicknames dürfen maximal 64 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten. Umlaute sind erlaubt." validation-error="Nicknames dürfen maximal 64 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten. Umlaute sind erlaubt."
/> />
<ValidationFormInput <ValidationFormInput
v-model="email" v-model="emailModel"
type="email" type="email"
label="E-Mail-Adresse" label="E-Mail-Adresse"
:placeholder="`z. B. lisa@${configStore.getConfig.community.domain}`" :placeholder="`z. B. lisa@${configStore.getConfig.community.domain}`"

View file

@ -2,6 +2,7 @@ import {
createRouter, createRouter,
createWebHistory, createWebHistory,
type LocationQueryRaw, type LocationQueryRaw,
type RouteLocationNormalized,
} from "vue-router"; } from "vue-router";
import AdminDashboardView from "@/views/AdminDashboardView.vue"; import AdminDashboardView from "@/views/AdminDashboardView.vue";
import AdminNodesView from "@/views/AdminNodesView.vue"; import AdminNodesView from "@/views/AdminNodesView.vue";
@ -9,10 +10,18 @@ import HomeView from "@/views/HomeView.vue";
import NodeCreateView from "@/views/NodeCreateView.vue"; import NodeCreateView from "@/views/NodeCreateView.vue";
import NodeDeleteView from "@/views/NodeDeleteView.vue"; import NodeDeleteView from "@/views/NodeDeleteView.vue";
import { import {
hasOwnProperty,
isFastdKey,
isHostname,
isMAC,
isNodesFilter, isNodesFilter,
isNodeSortField, isNodeSortField,
isSearchTerm,
isSortDirection, isSortDirection,
type SearchTerm, isString,
type JSONValue,
parseJSON,
type TypeGuard,
} from "@/types"; } from "@/types";
export interface Route { export interface Route {
@ -35,6 +44,39 @@ export function route(name: RouteName, query?: LocationQueryRaw): Route {
}; };
} }
function getQueryField<T>(
route: RouteLocationNormalized,
field: string,
isT: TypeGuard<T>
): T | undefined {
if (!hasOwnProperty(route.query, field)) {
return undefined;
}
const value = route.query[field];
return isT(value) ? value : undefined;
}
function getJSONQueryField<T>(
route: RouteLocationNormalized,
field: string,
isT: TypeGuard<T>
): T | undefined {
const value = getQueryField(route, field, isString);
if (!value) {
return undefined;
}
let json: JSONValue;
try {
json = parseJSON(value);
} catch (e) {
console.warn(e);
return undefined;
}
return isT(json) ? json : undefined;
}
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [
@ -47,6 +89,11 @@ const router = createRouter({
path: "/node/create", path: "/node/create",
name: RouteName.NODE_CREATE, name: RouteName.NODE_CREATE,
component: NodeCreateView, component: NodeCreateView,
props: (route) => ({
hostname: getQueryField(route, "hostname", isHostname),
fastdKey: getQueryField(route, "key", isFastdKey),
mac: getQueryField(route, "mac", isMAC),
}),
}, },
{ {
path: "/node/delete", path: "/node/delete",
@ -62,35 +109,12 @@ const router = createRouter({
path: "/admin/nodes", path: "/admin/nodes",
name: RouteName.ADMIN_NODES, name: RouteName.ADMIN_NODES,
component: AdminNodesView, component: AdminNodesView,
props: (route) => { props: (route) => ({
let filter: unknown; filter: getJSONQueryField(route, "filter", isNodesFilter) || {},
if ( searchTerm: getQueryField(route, "q", isSearchTerm),
Object.prototype.hasOwnProperty.call(route.query, "filter") sortDirection: getQueryField(route, "sortDir", isSortDirection),
) { sortField: getQueryField(route, "sortField", isNodeSortField),
try { }),
filter = JSON.parse(route.query.filter as string);
} catch (e) {
console.warn(e);
filter = {};
}
} else {
filter = {};
}
const searchTerm = route.query.q
? (route.query.q as SearchTerm)
: undefined;
return {
filter: isNodesFilter(filter) ? filter : {},
searchTerm,
sortDirection: isSortDirection(route.query.sortDir)
? route.query.sortDir
: undefined,
sortField: isNodeSortField(route.query.sortField)
? route.query.sortField
: undefined,
};
},
}, },
], ],
}); });

View file

@ -3,8 +3,15 @@ import PageContainer from "@/components/page/PageContainer.vue";
import NodeCreateForm from "@/components/nodes/NodeCreateForm.vue"; import NodeCreateForm from "@/components/nodes/NodeCreateForm.vue";
import NodeCreatedPanel from "@/components/nodes/NodeCreatedPanel.vue"; import NodeCreatedPanel from "@/components/nodes/NodeCreatedPanel.vue";
import { ref } from "vue"; import { ref } from "vue";
import type { StoredNode, Token } from "@/types"; import type { FastdKey, Hostname, MAC, StoredNode } from "@/types";
interface Props {
hostname?: Hostname;
fastdKey?: FastdKey;
mac?: MAC;
}
const props = defineProps<Props>();
const createdNode = ref<StoredNode | undefined>(undefined); const createdNode = ref<StoredNode | undefined>(undefined);
async function onCreate(node: StoredNode) { async function onCreate(node: StoredNode) {
@ -14,7 +21,13 @@ async function onCreate(node: StoredNode) {
<template> <template>
<PageContainer> <PageContainer>
<NodeCreateForm v-if="!createdNode" @create="onCreate" /> <NodeCreateForm
v-if="!createdNode"
@create="onCreate"
:hostname="props.hostname"
:fastdKey="props.fastdKey"
:mac="props.mac"
/>
<NodeCreatedPanel v-if="createdNode" :node="createdNode" /> <NodeCreatedPanel v-if="createdNode" :node="createdNode" />
</PageContainer> </PageContainer>
</template> </template>