Skip to content

Commit

Permalink
feat: ui refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
robot9706 committed Oct 9, 2023
1 parent 302fc9b commit e635ce6
Show file tree
Hide file tree
Showing 47 changed files with 477 additions and 651 deletions.
5 changes: 5 additions & 0 deletions web/backend/src/app/auth/auth.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { IsString, MinLength } from 'class-validator'

export interface AuthToken {
sub: string
username: string
}

export class LoginDto {
@IsString()
@MinLength(1)
Expand Down
26 changes: 23 additions & 3 deletions web/backend/src/app/auth/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { CanActivate, ExecutionContext, Injectable, SetMetadata, createParamDecorator } from '@nestjs/common'
import { CanActivate, ExecutionContext, Injectable, Logger, SetMetadata, createParamDecorator } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { Request } from 'express'
import { CruxUnauthorizedException } from 'src/exception/crux-exception'
import PrismaService from 'src/services/prisma.service'
import { AuthToken } from './auth.dto'

export const AUTH_COOKIE = 'auth'

Expand All @@ -12,29 +14,47 @@ export const Public = () => SetMetadata(IS_PUBLIC_KEY, true)

export const AuthUser = createParamDecorator((_: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest()
const user = request['user']
const user = request['user'] as AuthToken
return user?.sub
})

export const isAuthDisabled = (config: ConfigService) => config.get('DISABLE_AUTH') === 'true'

@Injectable()
export class AuthGuard implements CanActivate {
private static readonly logger = new Logger('AUTH')

constructor(
private jwtService: JwtService,
private reflector: Reflector,
private config: ConfigService,
private prisma: PrismaService,
) {}

async validateRequest(request: Request): Promise<boolean> {
const token = request.cookies?.[AUTH_COOKIE]
if (!token) {
AuthGuard.logger.warn('Request has no token')
return false
}
try {
const payload = await this.jwtService.verifyAsync(token)
const payload: AuthToken = await this.jwtService.verifyAsync(token)
const { sub } = payload

const user = await this.prisma.user.findFirst({
where: {
id: sub,
},
})

if (!user) {
AuthGuard.logger.warn(`Token has unknown user '${sub}'`)
return false
}

request['user'] = payload
} catch {
AuthGuard.logger.warn(`Token is invalid`)
return false
}
return true
Expand Down
5 changes: 3 additions & 2 deletions web/backend/src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import * as bcrypt from 'bcrypt'
import { CruxConflictException, CruxUnauthorizedException } from 'src/exception/crux-exception'
import { AuthToken } from './auth.dto'
import { UserService } from './user.service'

@Injectable()
Expand All @@ -19,7 +20,7 @@ export default class AuthService {
throw new CruxUnauthorizedException()
}

const payload = { sub: user.id, username: user.name }
const payload: AuthToken = { sub: user.id, username: user.name }
return await this.jwtService.signAsync(payload)
}

Expand All @@ -32,7 +33,7 @@ export default class AuthService {
const passwordHash = await this.hashPassword(pass)
const userId = await this.usersService.create(username, passwordHash)

const payload = { sub: userId, username, }
const payload: AuthToken = { sub: userId, username }
return await this.jwtService.signAsync(payload)
}

Expand Down
7 changes: 1 addition & 6 deletions web/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
"format": "prettier --write \"./**/*.tsx\" \"./**/*.ts\"",
"build": "react-scripts build",
"start": "react-scripts start",
"test:e2e": "playwright test",
"test:e2e-debug": "DEBUG=1 playwright test --debug",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"install:x64": "npm install --target_arch=x64"
"lint": "eslint src/**/*.ts src/**/*.tsx"
},
"dependencies": {
"clsx": "^2.0.0",
Expand Down
3 changes: 3 additions & 0 deletions web/frontend/src/assets/back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions web/frontend/src/assets/breadcrumb_home.svg

This file was deleted.

3 changes: 0 additions & 3 deletions web/frontend/src/assets/breadcrumb_next.svg

This file was deleted.

3 changes: 0 additions & 3 deletions web/frontend/src/assets/cog.svg

This file was deleted.

3 changes: 3 additions & 0 deletions web/frontend/src/assets/pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions web/frontend/src/assets/user.svg

This file was deleted.

3 changes: 3 additions & 0 deletions web/frontend/src/assets/view_json.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions web/frontend/src/assets/view_table.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 3 additions & 15 deletions web/frontend/src/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { Link, Outlet, useNavigate } from 'react-router-dom'
import { API_AUTH_LOGOUT, ROUTE_INDEX } from 'src/routes'
import DyoIcon from 'src/elements/dyo-icon'
import logo from 'src/assets/darklens_logo.svg'
import user from 'src/assets/user.svg'
import logout from 'src/assets/logout.svg'
import cog from 'src/assets/cog.svg'
import toast from 'react-hot-toast'
import { AuthContext } from './auth'

Expand Down Expand Up @@ -65,25 +63,15 @@ export const MainLayout = () => {

return (
<main className="w-full h-full">
<div className="bg-lens-surface-5 h-18 shadow-topbar fixed left-0 top-0 right-0 flex flex-row pr-7 z-50">
<div className="bg-lens-surface-6 h-18 shadow-topbar fixed left-0 top-0 right-0 flex flex-row pr-7 z-50">
<Link to={ROUTE_INDEX}>
<div className="px-12 bg-lens-surface-4">
<div className="px-12 bg-lens-surface-5">
<img className="cursor-pointer" src={logo} alt={t('common:logoAlt')} width={120} height={20} />
</div>
</Link>

<div className="flex-1 flex flex-row justify-end items-center">
<DyoIcon src={cog} alt="" size="lg" className="mr-2 cursor-pointer" />

{auth.hasAuth && (
<>
<DyoIcon src={user} alt="" size="lg" className="cursor-pointer" />

<div className="bg-lens-surface-3 w-px h-8 mx-3" />

<DyoIcon src={logout} alt="" size="lg" className="cursor-pointer" onClick={onLogout} />
</>
)}
{auth.hasAuth && <DyoIcon src={logout} alt="" size="lg" className="cursor-pointer" onClick={onLogout} />}
</div>
</div>

Expand Down
41 changes: 26 additions & 15 deletions web/frontend/src/components/nodes/dyo-node-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import DyoBadge from 'src/elements/dyo-badge'
import { DyoCard, DyoCardProps } from 'src/elements/dyo-card'
import DyoExpandableText from 'src/elements/dyo-expandable-text'
import { DyoHeading } from 'src/elements/dyo-heading'
import { DyoLabel } from 'src/elements/dyo-label'
import { DyoNode } from 'src/models'
import clsx from 'clsx'
import NodeStatusIndicator from './node-status-indicator'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import DyoIcon from 'src/elements/dyo-icon'
import editIcon from 'src/assets/pencil.svg'
import deleteIcon from 'src/assets/trash-can.svg'

interface DyoNodeCardProps extends Omit<DyoCardProps, 'children'> {
node: DyoNode
titleHref?: string
hideConnectionInfo?: boolean
onEdit: () => void
onDelete: () => void
}

const DyoNodeCard = (props: DyoNodeCardProps) => {
const { node, titleHref, className, hideConnectionInfo } = props

const { t } = useTranslation('common')
const { node, titleHref, className, onEdit, onDelete } = props

const title = (
<div className="flex flex-row">
Expand All @@ -31,27 +31,38 @@ const DyoNodeCard = (props: DyoNodeCardProps) => {
{node.name}
</DyoHeading>

{!hideConnectionInfo ? <NodeStatusIndicator className="place-items-center" status={node.status} /> : null}
<NodeStatusIndicator className="place-items-center" status={node.status} />
</div>
)

return (
<DyoCard className={clsx(className ?? 'p-6', 'flex flex-col')}>
<DyoCard className={clsx(className ?? 'p-6', 'flex flex-col relative parent-hover')}>
{titleHref ? <Link to={titleHref}>{title}</Link> : title}

{!hideConnectionInfo && node.address && (
<DyoLabel className="mr-auto mt-6">
{t(`address`)}: {node.address}
</DyoLabel>
)}

<DyoExpandableText
text={node.description}
lineClamp={node.address ? 4 : 6}
lineClamp={1}
className="text-md text-lens-text-2 mt-2 max-h-44"
buttonClassName="w-fit"
modalTitle={node.name}
/>

<div className="absolute right-0 bottom-0 pr-2">
<DyoIcon
src={editIcon}
alt=""
className="cursor-pointer mr-1 opacity-10 parent-hover-child"
size="lg"
onClick={onEdit}
/>
<DyoIcon
src={deleteIcon}
alt=""
className="cursor-pointer opacity-10 parent-hover-child"
size="lg"
onClick={onDelete}
/>
</div>
</DyoCard>
)
}
Expand Down
8 changes: 5 additions & 3 deletions web/frontend/src/components/nodes/edit-node-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useNodeState from './use-node-state'
import { WS_NODES, nodeApiTokenUrl } from 'src/routes'
import { useTranslation } from 'react-i18next'
import { useBackendDelete } from 'src/hooks/use-backend'
import NodeAuditList from './node-audit-list'

interface EditNodeSectionProps {
className?: string
Expand All @@ -31,7 +32,6 @@ const EditNodeSection = (props: EditNodeSectionProps) => {
const backendDelete = useBackendDelete()

const [revokeModalConfig, confirmTokenRevoke] = useConfirmation()

const [node, setNode] = useNodeState(
propsNode ??
({
Expand All @@ -41,8 +41,6 @@ const EditNodeSection = (props: EditNodeSectionProps) => {
} as NodeDetails),
)

const editing = !!node.id

const onNodeEdited = (newNode: NodeDetails, shouldClose?: boolean) => {
setNode(newNode)
propsOnNodeEdited(newNode, shouldClose)
Expand Down Expand Up @@ -109,6 +107,8 @@ const EditNodeSection = (props: EditNodeSectionProps) => {
onNodeEdited(newNode)
}

const editing = !!node.id

return (
<>
<div className={clsx(className, 'flex flex-row gap-4')}>
Expand Down Expand Up @@ -143,6 +143,8 @@ const EditNodeSection = (props: EditNodeSectionProps) => {
</div>
</div>

{propsNode && <NodeAuditList node={node} />}

<DyoConfirmationModal config={revokeModalConfig} className="w-1/4" />
</>
)
Expand Down
22 changes: 11 additions & 11 deletions web/frontend/src/components/nodes/inspect-table-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const KeyValueTable = (props: KeyValueTableProps) => {

const headers = ['common:key', 'common:value']

const defaultHeaderClass = 'uppercase text-lens-text-0 text-sm font-semibold bg-lens-surface-3.5 px-2 py-3 h-11'
const defaultHeaderClass = 'uppercase text-lens-text-0 text-sm font-semibold bg-lens-surface-5 px-2 py-3 h-11'
const headerClasses = [clsx('rounded-tl-lg pl-4', defaultHeaderClass), clsx('rounded-tr-lg pr-4', defaultHeaderClass)]

const columnWidths = ['w-1/2', 'w-1/2']
Expand All @@ -37,7 +37,7 @@ const KeyValueTable = (props: KeyValueTableProps) => {

return (
<DyoList
className="bg-lens-surface-4"
className="bg-lens-surface-6"
headers={headers.map(it => t(it))}
headerClassName={headerClasses}
columnWidths={columnWidths}
Expand Down Expand Up @@ -66,7 +66,7 @@ const MountsTable = (props: MountsTableProps) => {
'mountsInfo.propagation',
]

const defaultHeaderClass = 'uppercase text-lens-text-0 text-sm font-semibold bg-lens-surface-3.5 px-2 py-3 h-11'
const defaultHeaderClass = 'uppercase text-lens-text-0 text-sm font-semibold bg-lens-surface-5 px-2 py-3 h-11'
const headerClasses = [
clsx('rounded-tl-lg pl-4', defaultHeaderClass),
...Array.from({ length: headers.length - 2 }).map(() => defaultHeaderClass),
Expand Down Expand Up @@ -96,7 +96,7 @@ const MountsTable = (props: MountsTableProps) => {

return (
<DyoList
className="bg-lens-surface-4"
className="bg-lens-surface-6"
headers={headers.map(it => t(it))}
headerClassName={headerClasses}
columnWidths={columnWidths}
Expand All @@ -123,21 +123,21 @@ const InspectTableView = (props: InspectTableViewProps) => {
State: state,
Mounts: mounts,
} = inspect
const { Env: env, Labels: labels, Image: image, Hostname: hostname } = config
const { Networks: networks, IPAddress: ipAddress } = networkSettings
const { Status: status, ExitCode: exitCode } = state
const { Env: env, Labels: labels, Image: image, Hostname: hostname } = config ?? {}
const { Networks: networks, IPAddress: ipAddress } = networkSettings ?? {}
const { Status: status, ExitCode: exitCode } = state ?? {}

const { t } = useTranslation('nodes')

const envTableData = env.map(it => {
const envTableData = (env ?? []).map(it => {
const split = it.split('=')
return {
key: split[0],
value: split[1],
}
})

const labelsTableData = Object.entries(labels).map(([key, value]) => ({ key, value: value as string }))
const labelsTableData = Object.entries(labels ?? {}).map(([key, value]) => ({ key, value: value as string }))

const general: KeyValue[] = [
{
Expand Down Expand Up @@ -178,7 +178,7 @@ const InspectTableView = (props: InspectTableViewProps) => {
},
]

const networkTableData = Object.entries(networks).map(([networkName, network]) => {
const networkTableData = Object.entries(networks ?? {}).map(([networkName, network]) => {
const { IPAddress: ip, Gateway: gateway } = network as any

return {
Expand Down Expand Up @@ -212,7 +212,7 @@ const InspectTableView = (props: InspectTableViewProps) => {
</div>
<div className="flex flex-col">
<DyoLabel className="text-lg">{t('mounts')}</DyoLabel>
<MountsTable data={mounts} />
<MountsTable data={mounts ?? []} />
</div>
</>
)
Expand Down
Loading

0 comments on commit e635ce6

Please sign in to comment.