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:
Shahrad Elahi 2023-09-28 10:19:20 +03:30
parent 379d715ecd
commit b55cb8a025
5 changed files with 104 additions and 54 deletions

View File

@ -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"

View File

@ -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 */}

View File

@ -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'}>

View 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>
)
}

View File

@ -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'