Skip to content

Commit 2051cee

Browse files
author
hywax
committed
feat: service tags
1 parent 8b7cb02 commit 2051cee

File tree

6 files changed

+85
-5
lines changed

6 files changed

+85
-5
lines changed

components/service/base/Index.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
{{ description }}
2424
</slot>
2525
</p>
26+
<template v-if="tags.length">
27+
<ServiceBaseTag
28+
v-for="(tag, key) in tags"
29+
:key="key"
30+
:tag="tag"
31+
/>
32+
</template>
2633
</div>
2734
</Component>
2835
</template>

components/service/base/Tag.vue

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<template>
2+
<span :class="tagClasses">
3+
{{ tag.name }}
4+
</span>
5+
</template>
6+
7+
<script setup lang="ts">
8+
import type { Tag } from '~/types'
9+
10+
export interface Props {
11+
tag: Tag | string
12+
}
13+
14+
const props = defineProps<Props>()
15+
16+
const tag = computed<Tag>(() => (typeof props.tag === 'string' ? { name: props.tag, color: 'blue' } : props.tag))
17+
18+
const tagClasses = computed(() => ([
19+
`bg-${tag.value.color}-100`,
20+
`text-${tag.value.color}-800`,
21+
'text-xs',
22+
'font-medium',
23+
'me-2',
24+
'px-2.5',
25+
'py-0.5',
26+
'rounded',
27+
`dark:bg-${tag.value.color}-900`,
28+
`dark:text-${tag.value.color}-300`,
29+
]))
30+
</script>

server/utils/config.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,26 @@ import crypto from 'node:crypto'
22
import yaml from 'yaml'
33
import defu from 'defu'
44
import { ZodError, z } from 'zod'
5-
import type { CompleteConfig, Service } from '~/types'
5+
import type { CompleteConfig, Service, Tag } from '~/types'
66

77
type DraftService = Omit<Service, 'id'>
88

9-
function determineServiceId(items: DraftService[]): Service[] {
9+
type TagMap = Map<Tag['name'], Tag>
10+
11+
function determineService(items: DraftService[], tags: TagMap): Service[] {
1012
return items.map((item) => ({
11-
id: crypto.randomUUID(),
1213
...item,
14+
id: crypto.randomUUID(),
15+
tags: (item.tags || []).map((tag): Tag => {
16+
if (typeof tag === 'string') {
17+
return tags.get(tag) || {
18+
name: tag,
19+
color: 'blue',
20+
}
21+
}
22+
23+
return tag
24+
}),
1325
}))
1426
}
1527

@@ -22,6 +34,7 @@ export function getDefaultConfig(): CompleteConfig {
2234
behaviour: {
2335
target: '_blank',
2436
},
37+
tags: [],
2538
services: [],
2639
}
2740
}
@@ -40,6 +53,11 @@ export function validateConfigSchema(config: any) {
4053
color: z.string().optional(),
4154
})
4255

56+
const tag = z.object({
57+
name: z.string(),
58+
color: z.string(),
59+
})
60+
4361
const service = z.object({
4462
title: z.string().nullish().optional(),
4563
description: z.string().nullish().optional(),
@@ -57,6 +75,7 @@ export function validateConfigSchema(config: any) {
5775
lang: z.string().optional(),
5876
theme: z.string().optional(),
5977
checkUpdates: z.boolean().optional(),
78+
tags: z.array(tag).optional(),
6079
services: z.union([
6180
z.array(service),
6281
z.record(z.array(service)),
@@ -66,6 +85,14 @@ export function validateConfigSchema(config: any) {
6685
return schema.parse(config)
6786
}
6887

88+
function createTagMap(tags: Tag[]): TagMap {
89+
return tags.reduce((acc, tag) => {
90+
acc.set(tag.name, tag)
91+
92+
return acc
93+
}, new Map())
94+
}
95+
6996
export async function loadLocalConfig(): Promise<CompleteConfig> {
7097
const defaultConfig = getDefaultConfig()
7198
const storage = useStorage('data')
@@ -79,20 +106,21 @@ export async function loadLocalConfig(): Promise<CompleteConfig> {
79106
const raw = await storage.getItem<string>(file)
80107
const config = yaml.parse(raw || '') || {}
81108
const services: CompleteConfig['services'] = []
109+
const tags: TagMap = createTagMap(config.tags || [])
82110

83111
validateConfigSchema(config)
84112

85113
if (Array.isArray(config.services)) {
86114
services.push({
87-
items: determineServiceId(config.services),
115+
items: determineService(config.services, tags),
88116
})
89117
} else {
90118
const entries = Object.entries<DraftService[]>(config.services || [])
91119

92120
for (const [title, items] of entries) {
93121
services.push({
94122
title,
95-
items: determineServiceId(items),
123+
items: determineService(items, tags),
96124
})
97125
}
98126
}

tailwind.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import type { Config } from 'tailwindcss'
22

33
export default <Partial<Config>>{
44
darkMode: 'class',
5+
safelist: [
6+
{
7+
pattern: /(bg|text)-./,
8+
variants: ['dark'],
9+
},
10+
],
511
theme: {
612
extend: {
713
colors: {

types/config.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ export interface ServicesGroup {
55
items: Service[]
66
}
77

8+
export interface Tag {
9+
name: string
10+
color: 'red' | 'orange' | 'amber' | 'yellow' | 'lime' | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple' | 'fuchsia' | 'pink' | 'rose'
11+
}
12+
813
export interface Behaviour {
914
target?: '_blank' | '_self' | '_parent' | '_top'
1015
}
@@ -14,6 +19,7 @@ export interface Config {
1419
lang: 'en' | 'ru'
1520
theme?: 'system' | 'light' | 'dark' | 'deep' | 'sepia'
1621
behaviour?: Behaviour
22+
tags: Tag[]
1723
services: ServicesGroup[]
1824
checkUpdates: boolean
1925
}

types/services.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Tag } from '~/types/config'
2+
13
export interface ServiceStatus {
24
enabled?: boolean
35
interval?: number
@@ -20,6 +22,7 @@ export interface Service {
2022
target?: '_blank' | '_self' | '_parent' | '_top'
2123
icon?: ServiceIcon
2224
status?: ServiceStatus
25+
tags: Tag['name'][] | Tag[]
2326
options?: Record<string, any>
2427
secrets?: Record<string, any>
2528
server?: Record<string, any>

0 commit comments

Comments
 (0)