Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/npm_and_yarn/dashboard/vite-6.0.6
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-20 authored Jan 16, 2025
2 parents 34b939b + bde6a85 commit 9e78898
Show file tree
Hide file tree
Showing 16 changed files with 371 additions and 226 deletions.
9 changes: 9 additions & 0 deletions .local-dev/config/ns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ components:
type: traefik
traefik:
priorityOffset: 0
middleware:
sablier:
enable: true
url: http://sablier:10000
sessionDuration: 5m
dynamic:
theme: ghost
blocking:
timeout: 1m
tls:
certResolver: nsresolver
wildcard:
Expand Down
7 changes: 7 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ func init() {
viper.SetDefault("components.controller.docker.ss.url", "")
viper.SetDefault("components.controller.docker.routing.type", "traefik")
viper.SetDefault("components.controller.docker.routing.traefik.priorityOffset", 0)

viper.SetDefault("components.controller.docker.middleware.sablier.enable", true)
viper.SetDefault("components.controller.docker.middleware.sablier.url", "http://sablier:10000")
viper.SetDefault("components.controller.docker.middleware.sablier.sessionDuration", "1h")
viper.SetDefault("components.controller.docker.middleware.sablier.dynamic.theme", "ghost")
viper.SetDefault("components.controller.docker.middleware.sablier.blocking.timeout", "1m")

viper.SetDefault("components.controller.docker.tls.certResolver", "nsresolver")
viper.SetDefault("components.controller.docker.tls.wildcard.domains", nil)

Expand Down
10 changes: 10 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,14 @@ services:
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro

sablier:
image: sablierapp/sablier:1.8.1
command:
- start
- --provider.name=docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock

traefik:
image: traefik:3.1
restart: always
Expand All @@ -409,6 +417,8 @@ services:
- --ping=true
- --metrics.prometheus=true
- --metrics.prometheus.entrypoint=metrics
- --experimental.plugins.sablier.modulename=github.com/sablierapp/sablier
- --experimental.plugins.sablier.version=v1.8.1
ports:
- "80:80"
- "443:443"
Expand Down
5 changes: 5 additions & 0 deletions dashboard/src/components/templates/app/AppDeployInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const AppDeployInfo: Component<{
'bg-color-overlay-ui-primary-to-transparency-primary-selected hover:bg-color-overlay-ui-primary-to-transparency-primary-hover',
deploymentState(props.app) === ApplicationState.Idle &&
'bg-color-overlay-ui-primary-to-black-alpha-200 hover:bg-color-overlay-ui-primary-to-black-alpha-100',
deploymentState(props.app) === ApplicationState.Sleeping &&
'bg-color-overlay-ui-primary-to-violet-200 hover:bg-color-overlay-ui-primary-to-violet-100',
deploymentState(props.app) === ApplicationState.Deploying &&
'bg-color-overlay-ui-primary-to-transparency-warn-selected hover:bg-color-overlay-ui-primary-to-transparency-warn-hover',
deploymentState(props.app) === ApplicationState.Error &&
Expand Down Expand Up @@ -158,6 +160,9 @@ const AppDeployInfo: Component<{
<Show when={deploymentState(props.app) !== ApplicationState.Running}>
<List.RowData>現在アプリが起動していないためSSHアクセスはできません</List.RowData>
</Show>
<Show when={deploymentState(props.app) === ApplicationState.Sleeping}>
<List.RowData>アプリのURLにアクセスがあった場合、自動的に起動します</List.RowData>
</Show>
</List.RowContent>
</DeployInfoContainer>
</Show>
Expand Down
3 changes: 3 additions & 0 deletions dashboard/src/components/templates/app/AppStatusIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const components: Record<ApplicationState, (size: IconProps) => JSXElement> = {
style={{ 'font-size': `${props.size}px` }}
/>
),
[ApplicationState.Sleeping]: (props) => (
<div class="i-material-symbols:mode-standby shrink-0 text-violet-400" style={{ 'font-size': `${props.size}px` }} />
),
[ApplicationState.Running]: (props) => (
<div
class="i-material-symbols:check-circle shrink-0 text-accent-success"
Expand Down
37 changes: 32 additions & 5 deletions dashboard/src/components/templates/app/AppsFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { type Component, type ComponentProps, For, type Setter, Show } from 'sol
import { CheckBoxIcon } from '/@/components/UI/CheckBoxIcon'
import { RadioIcon } from '/@/components/UI/RadioIcon'
import { styled } from '/@/components/styled-components'
import { type ApplicationState, type RepositoryOrigin, originToIcon } from '/@/libs/application'
import {
type ApplicationState,
type RepoWithApp,
type RepositoryOrigin,
applicationState,
originToIcon,
repositoryURLToOrigin,
} from '/@/libs/application'
import { clsx } from '/@/libs/clsx'
import { allOrigins, allStatuses, sortItems } from '/@/pages/apps'
import { AppStatusIcon } from './AppStatusIcon'
Expand All @@ -19,6 +26,7 @@ const selectItemStyle = clsx(
const FilterItemContainer = styled('div', 'flex flex-col gap-2 text-bold text-text-black')

const AppsFilter: Component<{
allRepoWithApps: RepoWithApp[]
statuses: ApplicationState[]
setStatues: Setter<ApplicationState[]>
origin: RepositoryOrigin[]
Expand All @@ -31,6 +39,21 @@ const AppsFilter: Component<{
const filtered = () =>
props.statuses.length !== allStatuses.length || props.origin.length !== allOrigins.length || props.includeNoApp

const appCountByStatus = (status: ApplicationState): number => {
return props.allRepoWithApps
.filter((repo) => props.origin.includes(repositoryURLToOrigin(repo.repo.url)))
.flatMap((repo) => repo.apps.filter((app) => applicationState(app) === status)).length
}

const repoCountByOrigin = (origin: RepositoryOrigin): number => {
return props.allRepoWithApps
.filter((repo) => repositoryURLToOrigin(repo.repo.url) === origin)
.filter(
(repo) =>
props.includeNoApp || repo.apps.filter((app) => props.statuses.includes(applicationState(app))).length > 0,
).length
}

return (
<DropdownMenu.Root>
<DropdownMenu.Trigger
Expand Down Expand Up @@ -64,7 +87,7 @@ const AppsFilter: Component<{
}}
>
<FilterItemContainer class="grid-area-[status]">
Status
App Status
<ItemsContainer>
<For each={allStatuses}>
{(s) => (
Expand All @@ -84,15 +107,17 @@ const AppsFilter: Component<{
<CheckBoxIcon checked={props.statuses.includes(s.value)} />
</Checkbox.Indicator>
<AppStatusIcon state={s.value} hideTooltip />
{s.label}
<span>
{s.label} ({appCountByStatus(s.value)})
</span>
</Checkbox.Label>
</Checkbox.Root>
)}
</For>
</ItemsContainer>
</FilterItemContainer>
<FilterItemContainer class="grid-area-[provider]">
Origin
Repo Origin
<ItemsContainer>
<For each={allOrigins}>
{(s) => (
Expand All @@ -112,7 +137,9 @@ const AppsFilter: Component<{
<CheckBoxIcon checked={props.origin.includes(s.value)} />
</Checkbox.Indicator>
{originToIcon(s.value)}
{s.label}
<span>
{s.label} ({repoCountByOrigin(s.value)})
</span>
</Checkbox.Label>
</Checkbox.Root>
)}
Expand Down
63 changes: 61 additions & 2 deletions dashboard/src/libs/application.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { timestampDate } from '@bufbuild/protobuf/wkt'
import { AiFillGithub } from 'solid-icons/ai'
import { RiDevelopmentGitRepositoryLine } from 'solid-icons/ri'
import { SiGitea } from 'solid-icons/si'
Expand All @@ -9,6 +10,7 @@ import {
type CreateWebsiteRequest,
DeployType,
PortPublicationProtocol,
type Repository,
type Website,
} from '/@/api/neoshowcase/protobuf/gateway_pb'

Expand All @@ -25,6 +27,7 @@ export enum ApplicationState {
Idle = 'Idle',
Deploying = 'Deploying',
Running = 'Running',
Sleeping = 'Sleeping',
Serving = 'Serving',
Error = 'Error',
}
Expand All @@ -40,8 +43,8 @@ const autoShutdownEnabled = (app: Application): boolean => {
}

export const deploymentState = (app: Application): ApplicationState => {
// if app is not running or autoShutdown is enabled and container is missing, it's idle
if (!app.running || (autoShutdownEnabled(app) && app.container === Application_ContainerState.MISSING)) {
// App is not running
if (!app.running) {
return ApplicationState.Idle
}
if (app.currentBuild === '') {
Expand All @@ -51,12 +54,18 @@ export const deploymentState = (app: Application): ApplicationState => {
if (app.deployType === DeployType.RUNTIME) {
switch (app.container) {
case Application_ContainerState.MISSING:
// Has auto shutdown enabled, and the container is missing - app is sleeping, and will start on HTTP access
if (autoShutdownEnabled(app)) {
return ApplicationState.Sleeping
}
return ApplicationState.Deploying
case Application_ContainerState.STARTING:
return ApplicationState.Deploying
case Application_ContainerState.RUNNING:
return ApplicationState.Running
case Application_ContainerState.RESTARTING:
case Application_ContainerState.EXITED:
return ApplicationState.Idle
case Application_ContainerState.ERRORED:
case Application_ContainerState.UNKNOWN:
return ApplicationState.Error
Expand Down Expand Up @@ -140,3 +149,53 @@ export const portPublicationProtocolMap: Record<PortPublicationProtocol, string>
[PortPublicationProtocol.TCP]: 'TCP',
[PortPublicationProtocol.UDP]: 'UDP',
}

const newestAppDate = (apps: Application[]): number =>
Math.max(0, ...apps.map((a) => (a.updatedAt ? timestampDate(a.updatedAt).getTime() : 0)))
const compareRepoWithApp =
(sort: 'asc' | 'desc') =>
(a: RepoWithApp, b: RepoWithApp): number => {
// Sort by apps updated at
if (a.apps.length > 0 && b.apps.length > 0) {
if (sort === 'asc') {
return newestAppDate(a.apps) - newestAppDate(b.apps)
}
return newestAppDate(b.apps) - newestAppDate(a.apps)
}
// Bring up repositories with 1 or more apps at top
if ((a.apps.length > 0 && b.apps.length === 0) || (a.apps.length === 0 && b.apps.length > 0)) {
return b.apps.length - a.apps.length
}
// Fallback to sort by repository id
return a.repo.id.localeCompare(b.repo.id)
}

export interface RepoWithApp {
repo: Repository
apps: Application[]
}

export const useApplicationsFilter = (
repos: Repository[],
apps: Application[],
statuses: ApplicationState[],
origins: RepositoryOrigin[],
includeNoApp: boolean,
sort: 'asc' | 'desc',
): RepoWithApp[] => {
const filteredReposByOrigin = repos.filter((r) => origins.includes(repositoryURLToOrigin(r.url)))
const filteredApps = apps.filter((a) => statuses.includes(applicationState(a)))

const appsMap = {} as Record<string, Application[]>
for (const app of filteredApps) {
if (!appsMap[app.repositoryId]) appsMap[app.repositoryId] = []
appsMap[app.repositoryId].push(app)
}
const res = filteredReposByOrigin.reduce<RepoWithApp[]>((acc, repo) => {
if (!includeNoApp && !appsMap[repo.id]) return acc
acc.push({ repo, apps: appsMap[repo.id] || [] })
return acc
}, [])
res.sort(compareRepoWithApp(sort))
return res
}
Loading

0 comments on commit 9e78898

Please sign in to comment.