mirror of
https://github.com/wireadmin/wireadmin
synced 2025-03-09 13:20:39 +00:00
Adds a feature for removing duplicate lines in the torrc
file, and provides a few minor updates to the UI.
This commit is contained in:
parent
379d715ecd
commit
b55cb8a025
@ -35,16 +35,16 @@ EOF
|
|||||||
cat /etc/tor/bridges >>/etc/tor/torrc
|
cat /etc/tor/bridges >>/etc/tor/torrc
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Removing duplicated lines form /etc/tor/torrc file
|
|
||||||
awk '!seen[$0]++' /etc/tor/torrc >/tmp/torrc
|
|
||||||
mv /tmp/torrc /etc/tor/torrc
|
|
||||||
|
|
||||||
# any other environment variables that start with TOR_ are added to the torrc
|
# any other environment variables that start with TOR_ are added to the torrc
|
||||||
# file
|
# file
|
||||||
env | grep ^TOR_ | sed -e 's/TOR_//' -e 's/=/ /' >>/etc/tor/torrc
|
env | grep ^TOR_ | sed -e 's/TOR_//' -e 's/=/ /' >>/etc/tor/torrc
|
||||||
|
|
||||||
|
# Removing duplicated lines form /etc/tor/torrc file
|
||||||
|
awk '!seen[$0]++' /etc/tor/torrc >/tmp/torrc
|
||||||
|
mv /tmp/torrc /etc/tor/torrc
|
||||||
|
|
||||||
# Start Tor in the background
|
# Start Tor in the background
|
||||||
screen -L -Logfile /var/vlogs/tor -dmS tor bash -c "tor"
|
screen -L -Logfile /var/vlogs/tor -dmS tor bash -c "tor -f /etc/tor/torrc"
|
||||||
|
|
||||||
# Starting Redis server in detached mode
|
# Starting Redis server in detached mode
|
||||||
screen -L -Logfile /var/vlogs/redis -dmS redis bash -c "redis-server --port 6479 --daemonize no --dir /data --appendonly yes"
|
screen -L -Logfile /var/vlogs/redis -dmS redis bash -c "redis-server --port 6479 --daemonize no --dir /data --appendonly yes"
|
||||||
|
@ -17,6 +17,7 @@ import { getPeerConf } from "@lib/wireguard-utils";
|
|||||||
import EditableText from "@ui/EditableText";
|
import EditableText from "@ui/EditableText";
|
||||||
import { RLS_NAME_INPUT } from "@lib/form-rules";
|
import { RLS_NAME_INPUT } from "@lib/form-rules";
|
||||||
import { UPDATE_CLIENT } from "@lib/swr-fetch";
|
import { UPDATE_CLIENT } from "@lib/swr-fetch";
|
||||||
|
import CopiableWrapper from "@ui/CopiableWrapper";
|
||||||
|
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
@ -42,15 +43,13 @@ export default function ServerPage(props: PageProps) {
|
|||||||
async (url: string) => {
|
async (url: string) => {
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' }
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
router.replace('/').catch()
|
router.replace('/').catch()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const data = await resp.json() as APIResponse<any>
|
const data = await resp.json() as APIResponse<WgServer>
|
||||||
if (!data.ok) throw new Error('Server responded with error status')
|
if (!data.ok) throw new Error('Server responded with error status')
|
||||||
return data.result
|
return data.result
|
||||||
}
|
}
|
||||||
@ -58,17 +57,11 @@ export default function ServerPage(props: PageProps) {
|
|||||||
|
|
||||||
const { isMutating: isChangingStatus, trigger: changeStatus } = useSWRMutation(
|
const { isMutating: isChangingStatus, trigger: changeStatus } = useSWRMutation(
|
||||||
`/api/wireguard/${props.serverId}`,
|
`/api/wireguard/${props.serverId}`,
|
||||||
async (url: string, { arg }: {
|
async (url: string, { arg }: { arg: string }) => {
|
||||||
arg: string
|
|
||||||
}) => {
|
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
method: arg === 'remove' ? 'DELETE' : 'PUT',
|
method: arg === 'remove' ? 'DELETE' : 'PUT',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json'
|
body: arg === 'remove' ? undefined : JSON.stringify({ status: arg })
|
||||||
},
|
|
||||||
body: arg === 'remove' ? undefined : JSON.stringify({
|
|
||||||
status: arg
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
router.replace('/').catch()
|
router.replace('/').catch()
|
||||||
@ -98,7 +91,7 @@ export default function ServerPage(props: PageProps) {
|
|||||||
{ title: data ? data.name.toString() : 'LOADING...' }
|
{ title: data ? data.name.toString() : 'LOADING...' }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{error ? (
|
{error || (!isLoading && !data) ? (
|
||||||
<Card className={'flex items-center justify-center p-4'}>
|
<Card className={'flex items-center justify-center p-4'}>
|
||||||
! ERROR !
|
! ERROR !
|
||||||
</Card>
|
</Card>
|
||||||
@ -106,21 +99,26 @@ export default function ServerPage(props: PageProps) {
|
|||||||
<Card className={'flex items-center justify-center p-4'}>
|
<Card className={'flex items-center justify-center p-4'}>
|
||||||
Loading...
|
Loading...
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : data && (
|
||||||
<div className={'space-y-4'}>
|
<div className={'space-y-4'}>
|
||||||
<Card className={'[&>.ant-card-body]:max-md:p1-2'}>
|
<Card className={'[&>.ant-card-body]:max-md:p1-2'}>
|
||||||
<List>
|
<List>
|
||||||
<Row label={'IP address'}>
|
<Row label={'IP address'}>
|
||||||
<pre> {data.address}/24 </pre>
|
<pre> {data.address}/24 </pre>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row label={'Listen Port'}>
|
||||||
|
<pre> {data.listen} </pre>
|
||||||
|
</Row>
|
||||||
<Row label={'Status'}>
|
<Row label={'Status'}>
|
||||||
<StatusBadge status={data.status} />
|
<StatusBadge status={data.status} />
|
||||||
</Row>
|
</Row>
|
||||||
<Row label={'Public Key'}>
|
<Row label={'Public Key'}>
|
||||||
<MiddleEllipsis
|
<CopiableWrapper content={data.publicKey}>
|
||||||
content={data.publicKey}
|
<MiddleEllipsis
|
||||||
maxLength={16}
|
content={data.publicKey}
|
||||||
/>
|
maxLength={16}
|
||||||
|
/>
|
||||||
|
</CopiableWrapper>
|
||||||
</Row>
|
</Row>
|
||||||
</List>
|
</List>
|
||||||
<div className={'flex flex-wrap items-center gap-2 mt-6'}>
|
<div className={'flex flex-wrap items-center gap-2 mt-6'}>
|
||||||
@ -269,25 +267,26 @@ function Client(props: ClientProps) {
|
|||||||
<List.Item key={props.id} className={'flex items-center justify-between p-4'}>
|
<List.Item key={props.id} className={'flex items-center justify-between p-4'}>
|
||||||
<QRCodeModal ref={qrcodeRef} content={conf?.trim() || 'null'} />
|
<QRCodeModal ref={qrcodeRef} content={conf?.trim() || 'null'} />
|
||||||
<div className={'w-full flex flex-row items-center gap-x-2'}>
|
<div className={'w-full flex flex-row items-center gap-x-2'}>
|
||||||
<div className={'w-12 aspect-square flex items-center justify-center mr-4 rounded-full bg-gray-200'}>
|
|
||||||
{/* User Icon */}
|
<div
|
||||||
|
className={'w-12 aspect-square flex items-center justify-center mr-4 rounded-full bg-gray-200 max-md:hidden'}>
|
||||||
<i className={'fas fa-user text-gray-400 text-lg'} />
|
<i className={'fas fa-user text-gray-400 text-lg'} />
|
||||||
</div>
|
</div>
|
||||||
<div className={'col-span-12 md:col-span-4'}>
|
|
||||||
<div className={'col-span-12 md:col-span-4'}>
|
<div className={'flex flex-col items-start justify-between'}>
|
||||||
<EditableText
|
<EditableText
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
rules={RLS_NAME_INPUT}
|
rules={RLS_NAME_INPUT}
|
||||||
rootClassName={'font-medium col-span-4'}
|
rootClassName={'font-medium col-span-4'}
|
||||||
inputClassName={'w-20'}
|
inputClassName={'w-20'}
|
||||||
content={props.name}
|
content={props.name}
|
||||||
onChange={(v) => trigger({ name: v })}
|
onChange={(v) => trigger({ name: v })}
|
||||||
/>
|
/>
|
||||||
</div>
|
<CopiableWrapper content={props.allowedIps} className={'text-sm'} showInHover={true}>
|
||||||
<div className={'col-span-12 md:col-span-4'}>
|
|
||||||
<span className={'font-mono text-gray-400 text-xs'}> {props.allowedIps} </span>
|
<span className={'font-mono text-gray-400 text-xs'}> {props.allowedIps} </span>
|
||||||
</div>
|
</CopiableWrapper>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex items-center justify-center gap-x-3'}>
|
<div className={'flex items-center justify-center gap-x-3'}>
|
||||||
{/* QRCode */}
|
{/* QRCode */}
|
||||||
|
@ -15,6 +15,7 @@ import EditableText from "@ui/EditableText";
|
|||||||
import useSWRMutation from "swr/mutation";
|
import useSWRMutation from "swr/mutation";
|
||||||
import { UPDATE_SERVER } from "@lib/swr-fetch";
|
import { UPDATE_SERVER } from "@lib/swr-fetch";
|
||||||
import { RLS_NAME_INPUT } from "@lib/form-rules";
|
import { RLS_NAME_INPUT } from "@lib/form-rules";
|
||||||
|
import CopiableWrapper from "@ui/CopiableWrapper";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { data, error, isLoading, mutate } = useSWR(
|
const { data, error, isLoading, mutate } = useSWR(
|
||||||
@ -99,16 +100,26 @@ function ServerListItem(props: ServerListItemProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<List.Item className={'flex items-center justify-between p-4'}>
|
<List.Item className={'flex items-center justify-between p-4'}>
|
||||||
<div className={'w-full grid grid-cols-12 items-center gap-x-2'}>
|
<div className={'w-full grid grid-cols-8 md:grid-cols-12 items-center gap-x-1'}>
|
||||||
<ServerIcon type={props.type} className={'col-span-1'} />
|
<ServerIcon type={props.type} className={'max-md:hidden md:col-span-1'} />
|
||||||
<EditableText
|
<div className={'flex flex-col justify-between col-span-4'}>
|
||||||
disabled={isMutating}
|
<EditableText
|
||||||
rules={RLS_NAME_INPUT}
|
disabled={isMutating}
|
||||||
rootClassName={'font-medium col-span-4'}
|
rules={RLS_NAME_INPUT}
|
||||||
inputClassName={'w-20'}
|
rootClassName={'font-medium'}
|
||||||
content={props.name}
|
inputClassName={'w-full max-w-[120px]'}
|
||||||
onChange={(v) => trigger({ name: v })}
|
content={props.name}
|
||||||
/>
|
onChange={(v) => trigger({ name: v })}
|
||||||
|
/>
|
||||||
|
<CopiableWrapper
|
||||||
|
content={`${props.address}:${props.listen}`}
|
||||||
|
className={'text-sm'}
|
||||||
|
rootClassName={'mt-0.5'}
|
||||||
|
showInHover={true}
|
||||||
|
>
|
||||||
|
<span className={'font-mono text-gray-400 text-xs'}> {props.address}:{props.listen} </span>
|
||||||
|
</CopiableWrapper>
|
||||||
|
</div>
|
||||||
<div className={'col-span-4 justify-end'}>
|
<div className={'col-span-4 justify-end'}>
|
||||||
<StatusBadge status={props.status} />
|
<StatusBadge status={props.status} />
|
||||||
</div>
|
</div>
|
||||||
@ -129,13 +140,13 @@ type ServerIconProps = {
|
|||||||
|
|
||||||
function ServerIcon(props: ServerIconProps) {
|
function ServerIcon(props: ServerIconProps) {
|
||||||
return (
|
return (
|
||||||
<div className={props.className}>
|
<div className={twMerge('flex items-start', props.className)}>
|
||||||
<div className={'w-fit relative'}>
|
<div className={'w-fit h-full relative'}>
|
||||||
<Image
|
<Image
|
||||||
src={'/vps.29373866.svg'}
|
src={'/vps.29373866.svg'}
|
||||||
alt={'VPS'}
|
alt={'VPS'}
|
||||||
width={34}
|
width={40}
|
||||||
height={34}
|
height={40}
|
||||||
/>
|
/>
|
||||||
{props.type !== 'direct' && (
|
{props.type !== 'direct' && (
|
||||||
<div className={'absolute -bottom-1 -right-2 rounded-full bg-white'}>
|
<div className={'absolute -bottom-1 -right-2 rounded-full bg-white'}>
|
||||||
|
40
src/ui/CopiableWrapper.tsx
Normal file
40
src/ui/CopiableWrapper.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ReactHTMLProps } from "@lib/typings";
|
||||||
|
import { message } from "antd";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export interface CopiableWrapperProps extends Omit<ReactHTMLProps<HTMLSpanElement>, 'content'> {
|
||||||
|
rootClassName?: string
|
||||||
|
content: string | number
|
||||||
|
showInHover?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CopiableWrapper(props: CopiableWrapperProps) {
|
||||||
|
const {
|
||||||
|
content,
|
||||||
|
children,
|
||||||
|
rootClassName,
|
||||||
|
className,
|
||||||
|
showInHover = false,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
const [ messageApi, contextHolder ] = message.useMessage()
|
||||||
|
return (
|
||||||
|
<div className={twMerge('group flex items-center', rootClassName)}>
|
||||||
|
{contextHolder}
|
||||||
|
{children}
|
||||||
|
<i {...rest}
|
||||||
|
className={twMerge(
|
||||||
|
'ml-2 mb-0.5 far fa-copy cursor-pointer text-gray-400/80 hover:text-primary',
|
||||||
|
showInHover && 'group-hover:opacity-100 opacity-0',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(content.toString())
|
||||||
|
.then(() => messageApi.success('Copied!'))
|
||||||
|
.catch()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -39,7 +39,7 @@ export default function EditableText(props: EditableTextProps) {
|
|||||||
}, [ val ])
|
}, [ val ])
|
||||||
const [ form ] = Form.useForm()
|
const [ form ] = Form.useForm()
|
||||||
return (
|
return (
|
||||||
<div className={twMerge('group', rootClassName)}>
|
<div className={twMerge('group block', rootClassName)}>
|
||||||
<span {...rest} className={twMerge(
|
<span {...rest} className={twMerge(
|
||||||
editMode ? 'hidden' : 'flex items-center gap-x-2',
|
editMode ? 'hidden' : 'flex items-center gap-x-2',
|
||||||
'leading-none'
|
'leading-none'
|
||||||
|
Loading…
Reference in New Issue
Block a user