mirror of
https://github.com/gurucomputing/headscale-ui.git
synced 2026-01-23 10:35:45 +00:00
Compare commits
13 commits
2025.03.21
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f79824bf | ||
|
|
3c22ed935d | ||
|
|
c48c0a54fb | ||
|
|
fbb8b2b968 | ||
|
|
baffa0024c | ||
|
|
15c4dd9575 | ||
|
|
7dd92ab4ad | ||
|
|
5e97c52303 | ||
|
|
9516a519b8 | ||
|
|
625647ed19 | ||
|
|
907ab6af57 | ||
|
|
adef58f27d | ||
|
|
38e0de2696 |
18 changed files with 143 additions and 208 deletions
43
.github/workflows/publish-release.yaml
vendored
43
.github/workflows/publish-release.yaml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Variable Gathering
|
||||
id: gathervars
|
||||
|
|
@ -33,13 +33,13 @@ jobs:
|
|||
echo "NOT_PREVIOUSLY_PUBLISHED=$NOT_PREVIOUSLY_PUBLISHED" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker Image
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v6
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
with:
|
||||
build-args: |
|
||||
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
push: true
|
||||
|
||||
- name: Extract build out of docker image
|
||||
uses: shrink/actions-docker-extract@v2
|
||||
uses: shrink/actions-docker-extract@v3
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
id: extract
|
||||
with:
|
||||
|
|
@ -73,33 +73,14 @@ jobs:
|
|||
cd "${{ steps.extract.outputs.destination }}"
|
||||
7z a headscale-ui.zip web
|
||||
|
||||
- name: Create Draft Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.VERSION }}
|
||||
release_name: headscale-ui
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- name: upload asset to releases
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ steps.extract.outputs.destination }}/headscale-ui.zip
|
||||
asset_name: headscale-ui.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: publish release
|
||||
uses: eregon/publish-release@v1
|
||||
if: ${{ env.NOT_PREVIOUSLY_PUBLISHED != 0 }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
release_id: ${{ steps.create_release.outputs.id }}
|
||||
name: headscale-ui
|
||||
files: ${{ steps.extract.outputs.destination }}/headscale-ui.zip
|
||||
generate_release_notes: true
|
||||
make_latest: true
|
||||
|
|
@ -91,6 +91,7 @@ See [Other Configurations](/documentation/configuration.md) for further proxy ex
|
|||
The following versions correspond to the appropriate headscale version
|
||||
| Headscale Version | HS-UI Version |
|
||||
|-------------------|---------------|
|
||||
| 26+ | 2025-05-22+ |
|
||||
| 25+ | 2025-03-14+ |
|
||||
| 24+ | 2025-01-20+ |
|
||||
| 23+ | 2024-10-01+ |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# script variables
|
||||
OPENVSCODE_VERSION="1.98.0"
|
||||
OPENVSCODE_VERSION="1.103.1"
|
||||
OPENVSCODE_URL="https://github.com/gitpod-io/openvscode-server/releases/download/openvscode-server-v$OPENVSCODE_VERSION/openvscode-server-v$OPENVSCODE_VERSION-linux-x64.tar.gz"
|
||||
OPENVSCODE_RELEASE="openvscode-server-v$OPENVSCODE_VERSION-linux-x64"
|
||||
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "headscale-ui",
|
||||
"version": "2025.03.14",
|
||||
"version": "2025.07.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "headscale-ui",
|
||||
"version": "2025.03.14",
|
||||
"version": "2025.07.12",
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^4",
|
||||
"@sveltejs/adapter-static": "^3",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "headscale-ui",
|
||||
"version": "2025.03.21",
|
||||
"version": "2025.08.23",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port 8080 --host 0.0.0.0",
|
||||
"build": "vite build",
|
||||
|
|
@ -33,4 +33,4 @@
|
|||
"typescript": "^5"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@
|
|||
return apiKeys;
|
||||
}
|
||||
|
||||
export async function getPreauthKeys(userName: string): Promise<PreAuthKey[]> {
|
||||
export async function getPreauthKeys(userID: string): Promise<PreAuthKey[]> {
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
|
|
@ -341,7 +341,7 @@
|
|||
let headscalePreAuthKey = [new PreAuthKey()];
|
||||
let headscalePreAuthKeyResponse: Response = new Response();
|
||||
|
||||
await fetch(headscaleURL + endpointURL + '?user=' + userName, {
|
||||
await fetch(headscaleURL + endpointURL + '?user=' + userID, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -367,7 +367,7 @@
|
|||
return headscalePreAuthKey;
|
||||
}
|
||||
|
||||
export async function newPreAuthKey(userName: string, expiry: string, reusable: boolean, ephemeral: boolean): Promise<any> {
|
||||
export async function newPreAuthKey(userID: string, expiry: string, reusable: boolean, ephemeral: boolean): Promise<any> {
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
|
|
@ -381,7 +381,7 @@
|
|||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user: userName,
|
||||
user: userID,
|
||||
expiration: expiry,
|
||||
reusable: reusable,
|
||||
ephemeral: ephemeral
|
||||
|
|
@ -401,7 +401,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
export async function removePreAuthKey(userName: string, preAuthKey: string): Promise<any> {
|
||||
export async function removePreAuthKey(userID: string, preAuthKey: string): Promise<any> {
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
|
|
@ -416,7 +416,7 @@
|
|||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user: userName,
|
||||
user: userID,
|
||||
key: preAuthKey
|
||||
})
|
||||
})
|
||||
|
|
@ -464,7 +464,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
export async function moveDevice(deviceID: string, user: string): Promise<any> {
|
||||
export async function moveDevice(deviceID: string, userID: string): Promise<any> {
|
||||
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
|
|
@ -480,7 +480,7 @@
|
|||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user: user
|
||||
user: parseInt(userID)
|
||||
})
|
||||
})
|
||||
.then((response) => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ export class Device {
|
|||
public forcedTags: string[] = [];
|
||||
public validTags: string[] = [];
|
||||
public invalidTags: string[] = [];
|
||||
public user: { name: string } = { name: '' };
|
||||
public approvedRoutes: string[] = [];
|
||||
public availableRoutes: string[] = [];
|
||||
public subnetRoutes: string[] = [];
|
||||
public user: User = new User();
|
||||
public online?: boolean;
|
||||
|
||||
public constructor(init?: Partial<Device>) {
|
||||
|
|
@ -18,19 +21,7 @@ export class Device {
|
|||
export class ACL {
|
||||
public groups: { [key: string]: [string] } = {};
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class Route {
|
||||
// current (hs 18+) method of handling a route
|
||||
advertised: boolean = true;
|
||||
prefix: string = '';
|
||||
enabled: boolean = false;
|
||||
id: number = 0;
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
public constructor(init?: Partial<ACL>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +33,7 @@ export class APIKey {
|
|||
createdAt: string = '';
|
||||
lastSeen: string = '';
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
public constructor(init?: Partial<APIKey>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +56,7 @@ export class PreAuthKey {
|
|||
export class User {
|
||||
public id: string = '';
|
||||
public name: string = '';
|
||||
public email: string = '';
|
||||
public createdAt: string = '';
|
||||
public constructor(init?: Partial<User>) {
|
||||
Object.assign(this, init);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
<label class="block text-secondary text-sm font-bold mb-2" for="select">Select User</label>
|
||||
<select class="card-select mr-3" required bind:value={selectedUser}>
|
||||
{#each $userStore as user}
|
||||
<option>{user.name}</option>
|
||||
<option>{user.name.length > 1 ? user.name : user.email}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,67 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { getDeviceRoutes, modifyDeviceRoutes } from './DeviceRoutesAPI.svelte';
|
||||
import { Device, Route } from '$lib/common/classes';
|
||||
import { onMount } from 'svelte';
|
||||
import { alertStore } from '$lib/common/stores';
|
||||
import { Device } from '$lib/common/classes';
|
||||
import DeviceRoute from './DeviceRoutes/DeviceRoute.svelte';
|
||||
|
||||
export let device = new Device();
|
||||
let routesList: Route[] = [];
|
||||
let routeID = 0;
|
||||
|
||||
onMount(async () => {
|
||||
getDeviceRoutesAction();
|
||||
});
|
||||
|
||||
function getDeviceRoutesAction() {
|
||||
getDeviceRoutes(device.id)
|
||||
.then((routes) => {
|
||||
routesList = routes;
|
||||
})
|
||||
.catch((error) => {
|
||||
$alertStore = error;
|
||||
});
|
||||
}
|
||||
|
||||
function modifyDeviceRoutesAction() {
|
||||
modifyDeviceRoutes(device.id, routesList, routeID)
|
||||
.then((response) => {
|
||||
getDeviceRoutesAction();
|
||||
})
|
||||
.catch((error) => {
|
||||
$alertStore = error;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<th>Device Routes</th>
|
||||
<td
|
||||
><ul class="list-disc list-inside">
|
||||
{#each routesList as route, index}
|
||||
{#each device.availableRoutes as route}
|
||||
<li>
|
||||
{route.prefix}
|
||||
{#if route.enabled}
|
||||
<button
|
||||
on:click={() => {
|
||||
routesList[index].enabled = false;
|
||||
routeID = route.id;
|
||||
modifyDeviceRoutesAction();
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-xs tooltip capitalize bg-success text-success-content mx-1"
|
||||
data-tip="press to disable route">active</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
on:click={() => {
|
||||
routesList[index].enabled = true;
|
||||
routeID = route.id
|
||||
modifyDeviceRoutesAction();
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-xs tooltip capitalize bg-secondary text-secondary-content mx-1"
|
||||
data-tip="press to enable route">pending</button
|
||||
>
|
||||
{/if}
|
||||
<DeviceRoute {route} {device}></DeviceRoute>
|
||||
</li>
|
||||
{/each}
|
||||
</ul></td
|
||||
|
|
|
|||
60
src/lib/devices/DeviceCard/DeviceRoutes/DeviceRoute.svelte
Normal file
60
src/lib/devices/DeviceCard/DeviceRoutes/DeviceRoute.svelte
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<script>
|
||||
import { getDevices } from '$lib/common/apiFunctions.svelte';
|
||||
import { Device } from '$lib/common/classes';
|
||||
import { alertStore } from '$lib/common/stores';
|
||||
import { approveDeviceRoute } from './DeviceRouteAPI.svelte';
|
||||
|
||||
export let route = '';
|
||||
export let device = new Device();
|
||||
|
||||
let routeDisabled = false;
|
||||
function approveRouteAction() {
|
||||
approveDeviceRoute(device.id, [...device.approvedRoutes, route])
|
||||
.then(() => {
|
||||
// refresh users after editing
|
||||
getDevices();
|
||||
})
|
||||
.catch((error) => {
|
||||
$alertStore = error;
|
||||
});
|
||||
}
|
||||
|
||||
function removeRouteAction() {
|
||||
approveDeviceRoute(device.id, device.approvedRoutes.filter((r) => r !== route))
|
||||
.then(() => {
|
||||
// refresh users after editing
|
||||
getDevices();
|
||||
})
|
||||
.catch((error) => {
|
||||
$alertStore = error;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{route}
|
||||
{#if device.approvedRoutes.includes(route)}
|
||||
<button
|
||||
on:click={() => {
|
||||
routeDisabled = true;
|
||||
removeRouteAction();
|
||||
routeDisabled = false;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-xs tooltip capitalize bg-success text-success-content mx-1">active</button
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
on:click={() => {
|
||||
routeDisabled = true;
|
||||
approveRouteAction();
|
||||
routeDisabled = false;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-xs tooltip capitalize bg-secondary text-secondary-content mx-1"
|
||||
class:disabled={routeDisabled}
|
||||
data-tip="click to enable route">pending</button
|
||||
>
|
||||
{/if}
|
||||
{#if device.subnetRoutes.includes(route)}
|
||||
<button type="button" class="btn btn-xs tooltip capitalize bg-secondary text-secondary-content mx-1">subnet</button>
|
||||
{/if}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<script context="module" lang="ts">
|
||||
|
||||
export async function approveDeviceRoute(deviceID: string, routes: string[]): Promise<any> {
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
|
||||
let endpointURL = `/api/v1/node/${deviceID}/approve_routes`;
|
||||
|
||||
await fetch(headscaleURL + endpointURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
routes: routes
|
||||
})
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
// return the api data
|
||||
return response;
|
||||
} else {
|
||||
return response.text().then((text) => {
|
||||
throw JSON.parse(text).message;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<script context="module" lang="ts">
|
||||
import type { Route } from '$lib/common/classes';
|
||||
|
||||
export async function getDeviceRoutes(deviceID: string): Promise<Route[]> {
|
||||
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
|
||||
// endpoint url for getting users
|
||||
let endpointURL = `/api/v1/node/${deviceID}/routes`;
|
||||
|
||||
//returning variables
|
||||
let headscaleRouteList: Route[] = [];
|
||||
let headscaleDeviceResponse: Response = new Response();
|
||||
|
||||
await fetch(headscaleURL + endpointURL, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
// return the api data
|
||||
headscaleDeviceResponse = response;
|
||||
} else {
|
||||
return response.text().then((text) => {
|
||||
throw JSON.parse(text).message;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
await headscaleDeviceResponse.json().then((data) => {
|
||||
headscaleRouteList = data.routes;
|
||||
});
|
||||
return headscaleRouteList;
|
||||
}
|
||||
|
||||
export async function modifyDeviceRoutes(deviceID: string, routeList: Route[], routeID: number): Promise<any> {
|
||||
// variables in local storage
|
||||
let headscaleURL = localStorage.getItem('headscaleURL') || '';
|
||||
let headscaleAPIKey = localStorage.getItem('headscaleAPIKey') || '';
|
||||
let endpointURL = '';
|
||||
|
||||
routeList.forEach((route) => {
|
||||
if (route.id == routeID) {
|
||||
endpointURL = `/api/v1/routes/${routeID}/`;
|
||||
if (route.enabled) {
|
||||
endpointURL += 'enable';
|
||||
} else {
|
||||
endpointURL += 'disable';
|
||||
}
|
||||
}
|
||||
});
|
||||
//returning variables
|
||||
let headscaleDeviceResponse: Response = new Response();
|
||||
|
||||
await fetch(headscaleURL + endpointURL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${headscaleAPIKey}`
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
// return the api data
|
||||
headscaleDeviceResponse = response;
|
||||
} else {
|
||||
return response.text().then((text) => {
|
||||
throw JSON.parse(text).message;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
@ -34,9 +34,5 @@
|
|||
>
|
||||
</span>
|
||||
{/each}
|
||||
|
||||
{#each device.validTags as tag}
|
||||
<span class="mb-1 mr-1 btn btn-xs btn-secondary normal-case">{tag.replace("tag:","")}</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
export let device = new Device();
|
||||
let deviceMoving = false;
|
||||
let selectedUser = device.user.name;
|
||||
let selectedUser = device.user.id;
|
||||
|
||||
function moveDeviceAction() {
|
||||
moveDevice(device.id, selectedUser)
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
<form on:submit|preventDefault={moveDeviceAction}>
|
||||
<select class="card-select mr-3" required bind:value={selectedUser}>
|
||||
{#each $userStore as user}
|
||||
<option>{user.name}</option>
|
||||
<option value={user.id}>{user.name.length > 1 ? user.name : user.email}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<!-- edit accept symbol -->
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<td>{user.email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User Creation Date</th>
|
||||
<td>{new Date(user.createdAt)}</td>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
|
||||
function getPreauthKeysAction() {
|
||||
getPreauthKeys(user.name)
|
||||
getPreauthKeys(user.id)
|
||||
.then((keys) => {
|
||||
keyList = keys;
|
||||
})
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
<!-- Allow ability to expire if not expired -->
|
||||
{#if new Date(key.expiration).getTime() > new Date().getTime() && (!key.used || key.reusable)}
|
||||
<!-- trash symbol -->
|
||||
<button class="mr-2" on:click={() => {expirePreAuthKeyAction(user.name, key.key)}}
|
||||
<button class="mr-2" on:click={() => {expirePreAuthKeyAction(user.id, key.key)}}
|
||||
><svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 inline flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg></button
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
function NewPreAuthKeyAction() {
|
||||
let formattedDate = new Date(expiry).toISOString();
|
||||
newPreAuthKey(user.name, formattedDate, reusable, ephemeral)
|
||||
newPreAuthKey(user.id, formattedDate, reusable, ephemeral)
|
||||
.then(() => {
|
||||
newPreAuthKeyShow = false;
|
||||
getPreauthKeysAction();
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
|
||||
function getPreauthKeysAction() {
|
||||
getPreauthKeys(user.name)
|
||||
getPreauthKeys(user.id)
|
||||
.then((keys) => {
|
||||
keyList = keys;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
</script>
|
||||
|
||||
{#if !cardEditing}
|
||||
<span class="font-bold">{user.id}: {user.name}</span>
|
||||
<span class="font-bold">{user.id}: {user.name.length > 1 ? user.name : user.email}</span>
|
||||
<!-- edit symbol -->
|
||||
<button type="button" on:click|stopPropagation={() => editingUser()} class="ml-2"
|
||||
><svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue