feat: enable all remix future flags
This commit is contained in:
parent
1af292a5b0
commit
f623e7bc66
@ -10,6 +10,7 @@ import { loadContext } from './utils/config/headplane'
|
|||||||
|
|
||||||
await loadContext()
|
await loadContext()
|
||||||
|
|
||||||
|
export const streamTimeout = 5000
|
||||||
export default function handleRequest(
|
export default function handleRequest(
|
||||||
request: Request,
|
request: Request,
|
||||||
responseStatusCode: number,
|
responseStatusCode: number,
|
||||||
@ -27,7 +28,6 @@ export default function handleRequest(
|
|||||||
<RemixServer
|
<RemixServer
|
||||||
context={remixContext}
|
context={remixContext}
|
||||||
url={request.url}
|
url={request.url}
|
||||||
abortDelay={5000}
|
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
[isBot ? 'onAllReady' : 'onShellReady']() {
|
[isBot ? 'onAllReady' : 'onShellReady']() {
|
||||||
@ -57,6 +57,6 @@ export default function handleRequest(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
setTimeout(abort, 5000)
|
setTimeout(abort, streamTimeout + 1000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
3
app/routes.ts
Normal file
3
app/routes.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { flatRoutes } from '@remix-run/fs-routes'
|
||||||
|
|
||||||
|
export default flatRoutes()
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import { BeakerIcon, EyeIcon, IssueDraftIcon, PencilIcon } from '@primer/octicons-react'
|
import { BeakerIcon, EyeIcon, IssueDraftIcon, PencilIcon } from '@primer/octicons-react'
|
||||||
import { ActionFunctionArgs, json, LoaderFunctionArgs } from '@remix-run/node'
|
import { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'
|
||||||
import { useLoaderData, useRevalidator } from '@remix-run/react'
|
import { useLoaderData, useRevalidator } from '@remix-run/react'
|
||||||
import { useDebounceFetcher } from 'remix-utils/use-debounce-fetcher'
|
import { useDebounceFetcher } from 'remix-utils/use-debounce-fetcher'
|
||||||
import { useEffect, useState, useMemo } from 'react'
|
import { useEffect, useState, useMemo } from 'react'
|
||||||
@ -18,6 +18,7 @@ import { loadContext } from '~/utils/config/headplane'
|
|||||||
import { loadConfig } from '~/utils/config/headscale'
|
import { loadConfig } from '~/utils/config/headscale'
|
||||||
import { HeadscaleError, pull, put } from '~/utils/headscale'
|
import { HeadscaleError, pull, put } from '~/utils/headscale'
|
||||||
import { getSession } from '~/utils/sessions'
|
import { getSession } from '~/utils/sessions'
|
||||||
|
import { send } from '~/utils/res'
|
||||||
import log from '~/utils/log'
|
import log from '~/utils/log'
|
||||||
|
|
||||||
import { Editor, Differ } from './cm.client'
|
import { Editor, Differ } from './cm.client'
|
||||||
@ -116,9 +117,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
const session = await getSession(request.headers.get('Cookie'))
|
const session = await getSession(request.headers.get('Cookie'))
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return json({ success: false, error: null }, {
|
return send({ success: false, error: null }, 401)
|
||||||
status: 401,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -131,18 +130,18 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return json({ success: true, policy, error: null })
|
return { success: true, policy, error: null }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.debug('APIC', 'Failed to update ACL policy with error %s', error)
|
log.debug('APIC', 'Failed to update ACL policy with error %s', error)
|
||||||
|
|
||||||
// @ts-ignore: Shut UP we know it's a string most of the time
|
// @ts-ignore: Shut UP we know it's a string most of the time
|
||||||
const text = JSON.parse(error.message)
|
const text = JSON.parse(error.message)
|
||||||
return json({ success: false, error: text.message }, {
|
return send({ success: false, error: text.message }, {
|
||||||
status: error instanceof HeadscaleError ? error.status : 500,
|
status: error instanceof HeadscaleError ? error.status : 500,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return json({ success: true, error: null })
|
return { success: true, error: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|||||||
@ -43,16 +43,12 @@ export async function loader() {
|
|||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
const session = await getSession(request.headers.get('Cookie'))
|
const session = await getSession(request.headers.get('Cookie'))
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return json({ success: false }, {
|
return send({ success: false }, 401)
|
||||||
status: 401,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = await loadContext()
|
const context = await loadContext()
|
||||||
if (!context.config.write) {
|
if (!context.config.write) {
|
||||||
return json({ success: false }, {
|
return send({ success: false }, 403)
|
||||||
status: 403,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await request.json() as Record<string, unknown>
|
const data = await request.json() as Record<string, unknown>
|
||||||
@ -62,7 +58,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
await context.integration.onConfigChange(context.integration.context)
|
await context.integration.onConfigChange(context.integration.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
return json({ success: true })
|
return { success: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|||||||
@ -1,21 +1,20 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
import { ActionFunctionArgs } from '@remix-run/node'
|
||||||
import { ActionFunctionArgs, json } from '@remix-run/node'
|
|
||||||
|
|
||||||
import { del, post } from '~/utils/headscale'
|
import { del, post } from '~/utils/headscale'
|
||||||
import { getSession } from '~/utils/sessions'
|
import { getSession } from '~/utils/sessions'
|
||||||
|
import { send } from '~/utils/res'
|
||||||
import log from '~/utils/log'
|
import log from '~/utils/log'
|
||||||
|
|
||||||
export async function menuAction(request: ActionFunctionArgs['request']) {
|
export async function menuAction(request: ActionFunctionArgs['request']) {
|
||||||
const session = await getSession(request.headers.get('Cookie'))
|
const session = await getSession(request.headers.get('Cookie'))
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return json({ message: 'Unauthorized' }, {
|
return send({ message: 'Unauthorized' }, {
|
||||||
status: 401,
|
status: 401,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await request.formData()
|
const data = await request.formData()
|
||||||
if (!data.has('_method') || !data.has('id')) {
|
if (!data.has('_method') || !data.has('id')) {
|
||||||
return json({ message: 'No method or ID provided' }, {
|
return send({ message: 'No method or ID provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -26,17 +25,17 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case 'delete': {
|
case 'delete': {
|
||||||
await del(`v1/node/${id}`, session.get('hsApiKey')!)
|
await del(`v1/node/${id}`, session.get('hsApiKey')!)
|
||||||
return json({ message: 'Machine removed' })
|
return { message: 'Machine removed' }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'expire': {
|
case 'expire': {
|
||||||
await post(`v1/node/${id}/expire`, session.get('hsApiKey')!)
|
await post(`v1/node/${id}/expire`, session.get('hsApiKey')!)
|
||||||
return json({ message: 'Machine expired' })
|
return { message: 'Machine expired' }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'rename': {
|
case 'rename': {
|
||||||
if (!data.has('name')) {
|
if (!data.has('name')) {
|
||||||
return json({ message: 'No name provided' }, {
|
return send({ message: 'No name provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -44,12 +43,12 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
const name = String(data.get('name'))
|
const name = String(data.get('name'))
|
||||||
|
|
||||||
await post(`v1/node/${id}/rename/${name}`, session.get('hsApiKey')!)
|
await post(`v1/node/${id}/rename/${name}`, session.get('hsApiKey')!)
|
||||||
return json({ message: 'Machine renamed' })
|
return { message: 'Machine renamed' }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'routes': {
|
case 'routes': {
|
||||||
if (!data.has('route') || !data.has('enabled')) {
|
if (!data.has('route') || !data.has('enabled')) {
|
||||||
return json({ message: 'No route or enabled provided' }, {
|
return send({ message: 'No route or enabled provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -59,12 +58,12 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
const postfix = enabled ? 'enable' : 'disable'
|
const postfix = enabled ? 'enable' : 'disable'
|
||||||
|
|
||||||
await post(`v1/routes/${route}/${postfix}`, session.get('hsApiKey')!)
|
await post(`v1/routes/${route}/${postfix}`, session.get('hsApiKey')!)
|
||||||
return json({ message: 'Route updated' })
|
return { message: 'Route updated' }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'exit-node': {
|
case 'exit-node': {
|
||||||
if (!data.has('routes') || !data.has('enabled')) {
|
if (!data.has('routes') || !data.has('enabled')) {
|
||||||
return json({ message: 'No route or enabled provided' }, {
|
return send({ message: 'No route or enabled provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -77,12 +76,12 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
await post(`v1/routes/${route}/${postfix}`, session.get('hsApiKey')!)
|
await post(`v1/routes/${route}/${postfix}`, session.get('hsApiKey')!)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return json({ message: 'Exit node updated' })
|
return { message: 'Exit node updated' }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'move': {
|
case 'move': {
|
||||||
if (!data.has('to')) {
|
if (!data.has('to')) {
|
||||||
return json({ message: 'No destination provided' }, {
|
return send({ message: 'No destination provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -91,9 +90,9 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await post(`v1/node/${id}/user?user=${to}`, session.get('hsApiKey')!)
|
await post(`v1/node/${id}/user?user=${to}`, session.get('hsApiKey')!)
|
||||||
return json({ message: `Moved node ${id} to ${to}` })
|
return { message: `Moved node ${id} to ${to}` }
|
||||||
} catch {
|
} catch {
|
||||||
return json({ message: `Failed to move node ${id} to ${to}` }, {
|
return send({ message: `Failed to move node ${id} to ${to}` }, {
|
||||||
status: 500,
|
status: 500,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -110,10 +109,10 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
tags,
|
tags,
|
||||||
})
|
})
|
||||||
|
|
||||||
return json({ message: 'Tags updated' })
|
return { message: 'Tags updated' }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.debug('APIC', 'Failed to update tags: %s', error)
|
log.debug('APIC', 'Failed to update tags: %s', error)
|
||||||
return json({ message: 'Failed to update tags' }, {
|
return send({ message: 'Failed to update tags' }, {
|
||||||
status: 500,
|
status: 500,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -124,13 +123,13 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
const user = data.get('user')?.toString()
|
const user = data.get('user')?.toString()
|
||||||
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return json({ message: 'No machine key provided' }, {
|
return send({ message: 'No machine key provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return json({ message: 'No user provided' }, {
|
return send({ message: 'No user provided' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -145,12 +144,12 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
user, key,
|
user, key,
|
||||||
})
|
})
|
||||||
|
|
||||||
return json({
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Machine registered'
|
message: 'Machine registered'
|
||||||
})
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return json({
|
return send({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Failed to register machine'
|
message: 'Failed to register machine'
|
||||||
}, {
|
}, {
|
||||||
@ -160,7 +159,7 @@ export async function menuAction(request: ActionFunctionArgs['request']) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return json({ message: 'Invalid method' }, {
|
return send({ message: 'Invalid method' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { LoaderFunctionArgs, ActionFunctionArgs, json } from '@remix-run/node'
|
import { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node'
|
||||||
import { useLoaderData } from '@remix-run/react'
|
import { useLoaderData } from '@remix-run/react'
|
||||||
import { useLiveData } from '~/utils/useLiveData'
|
import { useLiveData } from '~/utils/useLiveData'
|
||||||
import { getSession } from '~/utils/sessions'
|
import { getSession } from '~/utils/sessions'
|
||||||
@ -7,6 +7,7 @@ import { PreAuthKey, User } from '~/types'
|
|||||||
import { pull, post } from '~/utils/headscale'
|
import { pull, post } from '~/utils/headscale'
|
||||||
import { loadContext } from '~/utils/config/headplane'
|
import { loadContext } from '~/utils/config/headplane'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { send } from '~/utils/res'
|
||||||
|
|
||||||
import Link from '~/components/Link'
|
import Link from '~/components/Link'
|
||||||
import TableList from '~/components/TableList'
|
import TableList from '~/components/TableList'
|
||||||
@ -19,7 +20,7 @@ import AuthKeyRow from './key'
|
|||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
const session = await getSession(request.headers.get('Cookie'))
|
const session = await getSession(request.headers.get('Cookie'))
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return json({ message: 'Unauthorized' }, {
|
return send({ message: 'Unauthorized' }, {
|
||||||
status: 401,
|
status: 401,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -32,7 +33,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
const user = data.get('user')
|
const user = data.get('user')
|
||||||
|
|
||||||
if (!key || !user) {
|
if (!key || !user) {
|
||||||
return json({ message: 'Missing parameters' }, {
|
return send({ message: 'Missing parameters' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -46,7 +47,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return json({ message: 'Pre-auth key expired' })
|
return { message: 'Pre-auth key expired' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creating a new pre-auth key
|
// Creating a new pre-auth key
|
||||||
@ -57,7 +58,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
const ephemeral = data.get('ephemeral')
|
const ephemeral = data.get('ephemeral')
|
||||||
|
|
||||||
if (!user || !expiry || !reusable || !ephemeral) {
|
if (!user || !expiry || !reusable || !ephemeral) {
|
||||||
return json({ message: 'Missing parameters' }, {
|
return send({ message: 'Missing parameters' }, {
|
||||||
status: 400,
|
status: 400,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -80,7 +81,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return json({ message: 'Pre-auth key created', key })
|
return { message: 'Pre-auth key created', key }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import { DataRef, DndContext, useDraggable, useDroppable } from '@dnd-kit/core'
|
import { DataRef, DndContext, useDraggable, useDroppable } from '@dnd-kit/core'
|
||||||
import { PersonIcon } from '@primer/octicons-react'
|
import { PersonIcon } from '@primer/octicons-react'
|
||||||
import { ActionFunctionArgs, json, LoaderFunctionArgs } from '@remix-run/node'
|
import { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'
|
||||||
import { useActionData, useLoaderData, useSubmit } from '@remix-run/react'
|
import { useActionData, useLoaderData, useSubmit } from '@remix-run/react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { ClientOnly } from 'remix-utils/client-only'
|
import { ClientOnly } from 'remix-utils/client-only'
|
||||||
@ -17,6 +17,7 @@ import { loadConfig } from '~/utils/config/headscale'
|
|||||||
import { del, post, pull } from '~/utils/headscale'
|
import { del, post, pull } from '~/utils/headscale'
|
||||||
import { getSession } from '~/utils/sessions'
|
import { getSession } from '~/utils/sessions'
|
||||||
import { useLiveData } from '~/utils/useLiveData'
|
import { useLiveData } from '~/utils/useLiveData'
|
||||||
|
import { send } from '~/utils/res'
|
||||||
|
|
||||||
import Auth from './auth'
|
import Auth from './auth'
|
||||||
import Oidc from './oidc'
|
import Oidc from './oidc'
|
||||||
@ -56,16 +57,12 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
const session = await getSession(request.headers.get('Cookie'))
|
const session = await getSession(request.headers.get('Cookie'))
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return json({ message: 'Unauthorized' }, {
|
return send({ message: 'Unauthorized' }, 401)
|
||||||
status: 401,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await request.formData()
|
const data = await request.formData()
|
||||||
if (!data.has('_method')) {
|
if (!data.has('_method')) {
|
||||||
return json({ message: 'No method provided' }, {
|
return send({ message: 'No method provided' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = String(data.get('_method'))
|
const method = String(data.get('_method'))
|
||||||
@ -73,9 +70,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case 'create': {
|
case 'create': {
|
||||||
if (!data.has('username')) {
|
if (!data.has('username')) {
|
||||||
return json({ message: 'No name provided' }, {
|
return send({ message: 'No name provided' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = String(data.get('username'))
|
const username = String(data.get('username'))
|
||||||
@ -83,39 +78,33 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
name: username,
|
name: username,
|
||||||
})
|
})
|
||||||
|
|
||||||
return json({ message: `User ${username} created` })
|
return { message: `User ${username} created` }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'delete': {
|
case 'delete': {
|
||||||
if (!data.has('username')) {
|
if (!data.has('username')) {
|
||||||
return json({ message: 'No name provided' }, {
|
return send({ message: 'No name provided' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = String(data.get('username'))
|
const username = String(data.get('username'))
|
||||||
await del(`v1/user/${username}`, session.get('hsApiKey')!)
|
await del(`v1/user/${username}`, session.get('hsApiKey')!)
|
||||||
return json({ message: `User ${username} deleted` })
|
return { message: `User ${username} deleted` }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'rename': {
|
case 'rename': {
|
||||||
if (!data.has('old') || !data.has('new')) {
|
if (!data.has('old') || !data.has('new')) {
|
||||||
return json({ message: 'No old or new name provided' }, {
|
return send({ message: 'No old or new name provided' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const old = String(data.get('old'))
|
const old = String(data.get('old'))
|
||||||
const newName = String(data.get('new'))
|
const newName = String(data.get('new'))
|
||||||
await post(`v1/user/${old}/rename/${newName}`, session.get('hsApiKey')!)
|
await post(`v1/user/${old}/rename/${newName}`, session.get('hsApiKey')!)
|
||||||
return json({ message: `User ${old} renamed to ${newName}` })
|
return { message: `User ${old} renamed to ${newName}` }
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'move': {
|
case 'move': {
|
||||||
if (!data.has('id') || !data.has('to') || !data.has('name')) {
|
if (!data.has('id') || !data.has('to') || !data.has('name')) {
|
||||||
return json({ message: 'No ID or destination provided' }, {
|
return send({ message: 'No ID or destination provided' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = String(data.get('id'))
|
const id = String(data.get('id'))
|
||||||
@ -124,18 +113,14 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await post(`v1/node/${id}/user?user=${to}`, session.get('hsApiKey')!)
|
await post(`v1/node/${id}/user?user=${to}`, session.get('hsApiKey')!)
|
||||||
return json({ message: `Moved ${name} to ${to}` })
|
return { message: `Moved ${name} to ${to}` }
|
||||||
} catch {
|
} catch {
|
||||||
return json({ message: `Failed to move ${name} to ${to}` }, {
|
return send({ message: `Failed to move ${name} to ${to}` }, 500)
|
||||||
status: 500,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return json({ message: 'Invalid method' }, {
|
return send({ message: 'Invalid method' }, 400)
|
||||||
status: 400,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { type ActionFunctionArgs, json, type LoaderFunctionArgs, redirect } from '@remix-run/node'
|
import { ActionFunctionArgs, LoaderFunctionArgs, redirect } from '@remix-run/node'
|
||||||
import { Form, useActionData, useLoaderData } from '@remix-run/react'
|
import { Form, useActionData, useLoaderData } from '@remix-run/react'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import Button from '~/components/Button'
|
|||||||
import Card from '~/components/Card'
|
import Card from '~/components/Card'
|
||||||
import Code from '~/components/Code'
|
import Code from '~/components/Code'
|
||||||
import TextField from '~/components/TextField'
|
import TextField from '~/components/TextField'
|
||||||
import { type Key } from '~/types'
|
import { Key } from '~/types'
|
||||||
import { loadContext } from '~/utils/config/headplane'
|
import { loadContext } from '~/utils/config/headplane'
|
||||||
import { pull } from '~/utils/headscale'
|
import { pull } from '~/utils/headscale'
|
||||||
import { startOidc } from '~/utils/oidc'
|
import { startOidc } from '~/utils/oidc'
|
||||||
@ -81,9 +81,9 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
return json({
|
return {
|
||||||
error: 'Invalid API key',
|
error: 'Invalid API key',
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
app/utils/res.ts
Normal file
5
app/utils/res.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { data } from '@remix-run/node'
|
||||||
|
|
||||||
|
export function send<T>(payload: T, init?: number | ResponseInit) {
|
||||||
|
return data(payload, init)
|
||||||
|
}
|
||||||
@ -17,8 +17,8 @@
|
|||||||
"@primer/octicons-react": "^19.12.0",
|
"@primer/octicons-react": "^19.12.0",
|
||||||
"@react-aria/toast": "3.0.0-beta.12",
|
"@react-aria/toast": "3.0.0-beta.12",
|
||||||
"@react-stately/toast": "3.0.0-beta.4",
|
"@react-stately/toast": "3.0.0-beta.4",
|
||||||
"@remix-run/node": "^2.13.1",
|
"@remix-run/node": "^2.15.0",
|
||||||
"@remix-run/react": "^2.13.1",
|
"@remix-run/react": "^2.15.0",
|
||||||
"@shopify/lang-jsonc": "^1.0.0",
|
"@shopify/lang-jsonc": "^1.0.0",
|
||||||
"@uiw/codemirror-theme-github": "^4.23.6",
|
"@uiw/codemirror-theme-github": "^4.23.6",
|
||||||
"@uiw/react-codemirror": "^4.23.6",
|
"@uiw/react-codemirror": "^4.23.6",
|
||||||
@ -41,7 +41,9 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@remix-run/dev": "^2.13.1",
|
"@remix-run/dev": "^2.15.0",
|
||||||
|
"@remix-run/fs-routes": "^2.15.0",
|
||||||
|
"@remix-run/route-config": "^2.15.0",
|
||||||
"@types/react": "npm:types-react@beta",
|
"@types/react": "npm:types-react@beta",
|
||||||
"@types/react-dom": "npm:types-react-dom@beta",
|
"@types/react-dom": "npm:types-react-dom@beta",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
|||||||
645
pnpm-lock.yaml
645
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,9 @@
|
|||||||
import { vitePlugin as remix } from '@remix-run/dev'
|
import { vitePlugin as remix } from '@remix-run/dev'
|
||||||
import { installGlobals } from '@remix-run/node'
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import babel from 'vite-plugin-babel'
|
import babel from 'vite-plugin-babel'
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||||
import { execSync } from 'node:child_process'
|
import { execSync } from 'node:child_process'
|
||||||
|
|
||||||
installGlobals()
|
|
||||||
|
|
||||||
const prefix = process.env.__INTERNAL_PREFIX || '/admin'
|
const prefix = process.env.__INTERNAL_PREFIX || '/admin'
|
||||||
if (prefix.endsWith('/')) {
|
if (prefix.endsWith('/')) {
|
||||||
throw new Error('Prefix must not end with a slash')
|
throw new Error('Prefix must not end with a slash')
|
||||||
@ -58,6 +55,14 @@ export default defineConfig(({ isSsrBuild }) => {
|
|||||||
plugins: [
|
plugins: [
|
||||||
remix({
|
remix({
|
||||||
basename: `${prefix}/`,
|
basename: `${prefix}/`,
|
||||||
|
future: {
|
||||||
|
v3_fetcherPersist: true,
|
||||||
|
v3_relativeSplatPath: true,
|
||||||
|
v3_throwAbortReason: true,
|
||||||
|
v3_lazyRouteDiscovery: true,
|
||||||
|
v3_singleFetch: true,
|
||||||
|
v3_routeConfig: true
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
babel({
|
babel({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user