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

View file

@ -2,6 +2,7 @@ import {
createRouter,
createWebHistory,
type LocationQueryRaw,
type RouteLocationNormalized,
} from "vue-router";
import AdminDashboardView from "@/views/AdminDashboardView.vue";
import AdminNodesView from "@/views/AdminNodesView.vue";
@ -9,10 +10,18 @@ import HomeView from "@/views/HomeView.vue";
import NodeCreateView from "@/views/NodeCreateView.vue";
import NodeDeleteView from "@/views/NodeDeleteView.vue";
import {
hasOwnProperty,
isFastdKey,
isHostname,
isMAC,
isNodesFilter,
isNodeSortField,
isSearchTerm,
isSortDirection,
type SearchTerm,
isString,
type JSONValue,
parseJSON,
type TypeGuard,
} from "@/types";
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({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
@ -47,6 +89,11 @@ const router = createRouter({
path: "/node/create",
name: RouteName.NODE_CREATE,
component: NodeCreateView,
props: (route) => ({
hostname: getQueryField(route, "hostname", isHostname),
fastdKey: getQueryField(route, "key", isFastdKey),
mac: getQueryField(route, "mac", isMAC),
}),
},
{
path: "/node/delete",
@ -62,35 +109,12 @@ const router = createRouter({
path: "/admin/nodes",
name: RouteName.ADMIN_NODES,
component: AdminNodesView,
props: (route) => {
let filter: unknown;
if (
Object.prototype.hasOwnProperty.call(route.query, "filter")
) {
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,
};
},
props: (route) => ({
filter: getJSONQueryField(route, "filter", isNodesFilter) || {},
searchTerm: getQueryField(route, "q", isSearchTerm),
sortDirection: getQueryField(route, "sortDir", isSortDirection),
sortField: getQueryField(route, "sortField", isNodeSortField),
}),
},
],
});

View file

@ -3,8 +3,15 @@ import PageContainer from "@/components/page/PageContainer.vue";
import NodeCreateForm from "@/components/nodes/NodeCreateForm.vue";
import NodeCreatedPanel from "@/components/nodes/NodeCreatedPanel.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);
async function onCreate(node: StoredNode) {
@ -14,7 +21,13 @@ async function onCreate(node: StoredNode) {
<template>
<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" />
</PageContainer>
</template>