feat: refactor client codebase in progress

This commit is contained in:
melvinchia3636
2026-05-30 11:44:18 +08:00
parent cd9435d55f
commit f75e253e59
107 changed files with 448 additions and 415 deletions

View File

@@ -12,4 +12,4 @@
],
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.experimental.useTsgo": false
}
}

Submodule apps/lifeforge--achievements added at 207155caa7

View File

@@ -1,9 +1,9 @@
{
"name": "@lifeforge/apps",
"private": true,
"description": "LifeForge modules",
"dependencies": {
"@lifeforge/TedMeadow--lang-ru": "workspace:*",
"@lifeforge/lifeforge--achievements": "workspace:*"
}
"name": "@lifeforge/apps",
"private": true,
"description": "LifeForge modules",
"dependencies": {
"@lifeforge/TedMeadow--lang-ru": "workspace:*",
"@lifeforge/lifeforge--achievements": "workspace:*"
}
}

View File

@@ -16,7 +16,7 @@ function ErrorScreen({
errorMessage: string
}) {
return (
<Stack align="center" gap="lg">
<Stack align="center" gap="lg" height="100dvh">
<Flex
centered
height="5em"
@@ -26,7 +26,7 @@ function ErrorScreen({
}}
width="5em"
>
<Icon color="dangerous" icon="tabler:alert-triangle" size="2.25em" />
<Icon color="dangerous" icon="tabler:alert-circle" size="2.25em" />
</Flex>
<Text size="xl" weight="medium">
{errorMessage}

View File

@@ -54,11 +54,9 @@ function useQRLoginApproval(scannedData: string) {
if (!sessionData) return
try {
const response = await forgeAPI
.user.qrLogin.approveQRLogin
.mutate({
sessionId: sessionData.sessionId
} as never)
const response = await forgeAPI.user.qrLogin.approveQRLogin.mutate({
sessionId: sessionData.sessionId
} as never)
setBrowserInfo(response.browserInfo)
setStep('success')

View File

@@ -69,79 +69,83 @@ function DashboardGrid({
}
return (
<ResponsiveGridLayout
autoSize
className={canLayoutChange ? 'pb-64' : undefined}
cols={{
lg: 8,
md: 8,
sm: 4,
xs: 4,
xxs: 4
}}
containerPadding={[0, 0]}
isDraggable={canLayoutChange}
isDroppable={canLayoutChange}
isResizable={canLayoutChange}
layouts={enabledWidgets}
margin={[10, 10]}
rowHeight={100}
width={width}
onLayoutChange={(_, layouts) => {
changeDashboardLayout(layouts as never)
}}
<Box
asChild
style={canLayoutChange ? { paddingBottom: '16em' } : undefined}
>
{[
...new Set(
Object.values(enabledWidgets)
.map(widgetArray => widgetArray.map(widget => widget.i))
.flat()
)
].map(widgetId => (
<div
key={widgetId}
className={clsx('relative', canLayoutChange && 'cursor-move')}
>
{(() => {
if (!width || !height) {
return null
}
<ResponsiveGridLayout
autoSize
cols={{
lg: 8,
md: 8,
sm: 4,
xs: 4,
xxs: 4
}}
containerPadding={[0, 0]}
isDraggable={canLayoutChange}
isDroppable={canLayoutChange}
isResizable={canLayoutChange}
layouts={enabledWidgets}
margin={[10, 10]}
rowHeight={100}
width={width}
onLayoutChange={(_, layouts) => {
changeDashboardLayout(layouts as never)
}}
>
{[
...new Set(
Object.values(enabledWidgets)
.map(widgetArray => widgetArray.map(widget => widget.i))
.flat()
)
].map(widgetId => (
<div
key={widgetId}
className={clsx('relative', canLayoutChange && 'cursor-move')}
>
{(() => {
if (!width || !height) {
return null
}
const Component = (COMPONENTS[
widgetId as keyof typeof COMPONENTS
] ?? NotFoundWidget) as React.FC<{
dimension: { w: number; h: number }
widgetId?: string
}>
const Component = (COMPONENTS[
widgetId as keyof typeof COMPONENTS
] ?? NotFoundWidget) as React.FC<{
dimension: { w: number; h: number }
widgetId?: string
}>
const dimension = (
enabledWidgets[getBreakpointFromWidth(width)] || []
).find(l => l.i === widgetId)
const dimension = (
enabledWidgets[getBreakpointFromWidth(width)] || []
).find(l => l.i === widgetId)
return (
<Component
dimension={{
w: dimension?.w ?? 0,
h: dimension?.h ?? 0
}}
widgetId={widgetId}
/>
)
})()}
{canLayoutChange && (
<>
<div className="bg-bg-900/30 absolute inset-0 top-0 left-0 rounded" />
<Box bottom="0" position="absolute" right="0">
<Icon
className="1.5em"
icon="clarity:drag-handle-corner-line"
return (
<Component
dimension={{
w: dimension?.w ?? 0,
h: dimension?.h ?? 0
}}
widgetId={widgetId}
/>
</Box>
</>
)}
</div>
))}
</ResponsiveGridLayout>
)
})()}
{canLayoutChange && (
<>
<div className="bg-bg-900/30 absolute inset-0 top-0 left-0 rounded" />
<Box bottom="0" position="absolute" right="0">
<Icon
className="1.5em"
icon="clarity:drag-handle-corner-line"
/>
</Box>
</>
)}
</div>
))}
</ResponsiveGridLayout>
</Box>
)
}

View File

@@ -0,0 +1,43 @@
import React from 'react'
import { useMainSidebarState } from '@lifeforge/shared'
import { Button, Flex, Text } from '@lifeforge/ui'
function SaveButtonPopup({
canChange,
setCanChange
}: {
canChange: boolean
setCanChange: React.Dispatch<React.SetStateAction<boolean>>
}) {
const { sidebarExpanded } = useMainSidebarState()
if (!canChange) return null
return (
<Flex
centered
shadow
bg={{ base: 'bg-100', dark: 'bg-900' }}
bottom="1.5em"
gap="md"
left="50%"
p="md"
position="absolute"
r="lg"
style={{
transform: 'translateX(-50%)'
}}
zIndex="50"
>
<Text as="p" className="font-medium" weight="medium">
You are editing dashboard layout
</Text>
<Button icon="tabler:device-floppy" onClick={() => setCanChange(false)}>
Save
</Button>
</Flex>
)
}
export default SaveButtonPopup

View File

@@ -1,7 +1,6 @@
import { useCallback, useRef, useState } from 'react'
import {
Button,
ContextMenuItem,
Flex,
ModuleHeader,
@@ -9,6 +8,7 @@ import {
} from '@lifeforge/ui'
import DashboardGrid from './components/DashboardGrid'
import SaveButtonPopup from './components/SaveButtonPopup'
import './index.css'
import ManageWidgetsModal from './modals/ManageWidgetsModal'
import WidgetProvider, { useWidgets } from './providers/WidgetProvider'
@@ -56,25 +56,10 @@ function DashboardContent() {
/>
{/* Save Button Popup */}
{canLayoutChange && (
<div className="fixed right-6 bottom-6 z-50">
<div className="bg-bg-100 dark:bg-bg-800 flex items-center gap-4 rounded-md p-4 shadow-lg">
<div className="min-w-0">
<div className="text-sm font-medium">
You are Editing Dashboard Layout
</div>
</div>
<div className="flex items-center gap-2">
<Button
icon="tabler:device-floppy"
onClick={() => setCanLayoutChange(false)}
>
Save
</Button>
</div>
</div>
</div>
)}
<SaveButtonPopup
canChange={canLayoutChange}
setCanChange={setCanLayoutChange}
/>
</Flex>
)
}

View File

@@ -3,7 +3,16 @@ import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { type IDashboardLayout, usePersonalization } from '@lifeforge/shared'
import { Icon, Switch } from '@lifeforge/ui'
import {
COLORS,
Card,
Flex,
Icon,
Switch,
Text,
Transition,
withOpacity
} from '@lifeforge/ui'
import { useUserPersonalization } from '@/providers/features/UserPersonalizationProvider'
@@ -106,21 +115,49 @@ function ComponentListItem({
}
return (
<li
className="flex-between bg-bg-50 shadow-custom dark:bg-bg-800/50 flex gap-8 rounded-lg p-4"
<Card
shadow
align="center"
as="li"
bg={{
base: 'bg-50',
dark: 'bg-800'
}}
direction="row"
gap="xl"
justify="between"
onClick={toggleComponent}
>
<div className="flex items-center gap-3">
<div
className={clsx(
'flex size-10 shrink-0 items-center justify-center rounded-lg transition-all',
Object.keys(enabledWidgets).includes(id)
? 'bg-custom-500/20 text-custom-500'
: 'bg-bg-200 text-bg-400 dark:bg-bg-700/50 dark:text-bg-500'
)}
>
<Icon icon={icon} size="1.5em" />
</div>
<Flex align="center" gap="md">
<Transition>
<Flex
centered
className={clsx(
Object.keys(enabledWidgets).includes(id)
? 'bg-custom-500/20'
: 'bg-bg-200 dark:bg-bg-700/50'
)}
flexShrink="0"
height="2.5em"
r="lg"
style={{
backgroundColor: Object.keys(enabledWidgets).includes(id)
? withOpacity(COLORS['custom-500'], 0.2)
: ''
}}
width="2.5em"
>
<Icon
color={
Object.keys(enabledWidgets).includes(id)
? 'custom-500'
: 'bg-500'
}
icon={icon}
size="1.5em"
/>
</Flex>
</Transition>
<div className="flex flex-col">
<div className="font-semibold">
{t([
@@ -131,21 +168,21 @@ function ComponentListItem({
id
])}
</div>
<div className="text-bg-500 text-sm">
<Text color="muted" size="sm">
{t([
`widgets.${namespace}.${id}.description`,
`widgets.${id}.description`
])}
</div>
</Text>
</div>
</div>
</Flex>
<Switch
value={isEnabled}
onChange={() => {
toggleComponent()
}}
/>
</li>
</Card>
)
}

View File

@@ -1,4 +1,4 @@
import { ModalHeader } from '@lifeforge/ui'
import { Box, ModalHeader, Stack } from '@lifeforge/ui'
import type { WidgetEntry } from '../../providers/WidgetProvider'
import ComponentListItem from './components/ComponentItem'
@@ -11,14 +11,14 @@ function ManageWidgetsModal({
data: { widgets: Record<string, WidgetEntry> }
}) {
return (
<div className="min-w-[40vw]">
<Box minWidth="40vw">
<ModalHeader
icon="tabler:apps"
namespace="common.dashboard"
title="Manage Widgets"
onClose={onClose}
/>
<ul className="space-y-2 overflow-y-auto">
<Stack as="ul">
{Object.entries(widgets).map(
([key, { icon, minW, minH, maxW, maxH, namespace }]) => (
<ComponentListItem
@@ -33,8 +33,8 @@ function ManageWidgetsModal({
/>
)
)}
</ul>
</div>
</Stack>
</Box>
)
}

View File

@@ -60,11 +60,9 @@ function BgImageSelector() {
async function onSubmit(file: string | File) {
try {
const data = await forgeAPI
.user.personalization.updateBgImage
.mutate({
file
})
const data = await forgeAPI.user.personalization.updateBgImage.mutate({
file
})
setBgImage(forgeAPI.getMedia(data))
toast.success('Background image updated')

View File

@@ -29,18 +29,16 @@ function FontListItem({
const queryClient = useQueryClient()
const togglePinMutation = useMutation(
forgeAPI
.user.personalization.toggleGoogleFontsPin
.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['user', 'personalization', 'listGoogleFontsPin']
})
},
onError: () => {
toast.error('Failed to toggle font pin')
}
})
forgeAPI.user.personalization.toggleGoogleFontsPin.mutationOptions({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['user', 'personalization', 'listGoogleFontsPin']
})
},
onError: () => {
toast.error('Failed to toggle font pin')
}
})
)
const [loadingPin, handleTogglePin] = usePromiseLoading(async () => {

View File

@@ -30,7 +30,6 @@ export default function CoreFederationProvider({
try {
const result = await loadModules()
setModules(result.routes)
setGlobalProviders(result.globalProviders)
setCategoryTranslations(result.categoryTranslations)

View File

@@ -4,24 +4,39 @@ import { initReactI18next } from 'react-i18next'
import forgeAPI from './forgeAPI'
const AVAILABLE_LANG = (await fetch(
`${import.meta.env.VITE_API_HOST}/locales/listLanguages`
)
.then(res => res.json())
.then(data => data.data)
.catch(err => {
console.warn('Failed to fetch available languages from backend:', err)
return [{ name: 'en', icon: 'twemoji:flag-united-states' }]
})) as {
export let AVAILABLE_LANG: {
name: string
alternative?: string[]
icon: string
}[]
}[] = [{ name: 'en', icon: 'circle-flags:gb' }]
i18n
.use(I18NextHttpBackend)
.use(initReactI18next)
.init({
i18n.use(I18NextHttpBackend).use(initReactI18next)
export async function initI18n() {
if (i18n.isInitialized) {
return i18n
}
try {
const langRes = await fetch(
`${import.meta.env.VITE_API_HOST}/locales/listLanguages`
)
if (langRes.ok) {
const data = await langRes.json()
if (data?.data) {
AVAILABLE_LANG = data.data
}
}
} catch (err) {
console.warn(
'Failed to fetch available languages, falling back to default:',
err
)
}
await i18n.init({
lng: 'en',
fallbackLng: 'en',
cache: {
@@ -82,8 +97,6 @@ i18n
}
}
})
.catch(err => {
console.error('Failed to initialize i18n: ', err)
})
export default i18n
return i18n
}

View File

@@ -1,2 +1,3 @@
import './index.css'
import('./bootstrap')

View File

@@ -0,0 +1,37 @@
import { useEffect, useState } from 'react'
import { ErrorScreen, LoadingScreen } from '@lifeforge/ui'
import { initI18n } from '@/i18n'
export default function I18nInitProvider({
children
}: {
children: React.ReactNode
}) {
const [initialized, setInitialized] = useState<boolean | 'error'>(false)
useEffect(() => {
initI18n()
.then(() => {
setInitialized(true)
})
.catch(err => {
console.error('Failed to initialize i18n:', err)
setInitialized('error')
})
}, [])
if (!initialized) {
return <LoadingScreen message="Initializing localization..." />
}
if (initialized === 'error') {
return (
<ErrorScreen message="Failed to initialized localization service. Please check your i18n config." />
)
}
return children
}

View File

@@ -24,11 +24,9 @@ async function syncUserData(
setUserData: React.Dispatch<React.SetStateAction<any>>
) {
try {
await forgeAPI
.user.personalization.updatePersonalization
.mutate({
data
})
await forgeAPI.user.personalization.updatePersonalization.mutate({
data
})
if (setUserData) {
setUserData((oldData: any) => {

View File

@@ -26,6 +26,7 @@ import forgeAPI from '@/forgeAPI'
import AppRoutesProvider from '@/routes/providers/AppRoutesProvider'
import ExternalModuleProviders from './features/ExternalModuleProviders'
import I18nInitProvider from './features/I18nInitProvider'
import UserPersonalizationProvider from './features/UserPersonalizationProvider'
import { constructComponentTree, defineProviders } from './utils/providerUtils'
@@ -47,10 +48,6 @@ function Providers() {
// Provider that tells components the API endpoint to use
[APIEndpointProvider, { endpoint: import.meta.env.VITE_API_HOST }],
// Provider that initializes end-to-end encryption (fetches server public key)
[EncryptionProvider, { apiHost: import.meta.env.VITE_API_HOST }],
[EncryptionWrapper],
// Provider that stores all the theming information
[PersonalizationProvider, { forgeAPI }],
@@ -66,6 +63,13 @@ function Providers() {
],
[APIOnlineStatusWrapper],
// All subsequent providers are gated behind the API status check!
[I18nInitProvider],
// Provider that initializes end-to-end encryption (fetches server public key)
[EncryptionProvider, { apiHost: import.meta.env.VITE_API_HOST }],
[EncryptionWrapper],
// Provider that handles authentication, very obviously
[
AuthProvider,

View File

@@ -2,17 +2,10 @@
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": [
"ES2020",
"DOM",
"DOM.Iterable",
"ES2024.Object"
],
"lib": ["ES2020", "DOM", "DOM.Iterable", "ES2024.Object"],
"module": "ESNext",
"skipLibCheck": true,
"types": [
"node"
],
"types": ["node"],
/* Bundler mode */
"outDir": "${configDir}/tsbuild",
"moduleResolution": "bundler",
@@ -29,15 +22,9 @@
"rootDir": "../",
"baseUrl": "./",
"paths": {
"@/*": [
"./src/*"
],
"@core/*": [
"../core/*"
],
"@functions/*": [
"../server/src/core/functions/*"
],
"@/*": ["./src/*"],
"@core/*": ["../core/*"],
"@functions/*": ["../server/src/core/functions/*"]
}
},
"references": [
@@ -45,7 +32,5 @@
"path": "../server"
}
],
"include": [
"./src/**/*"
]
}
"include": ["./src/**/*"]
}

View File

@@ -27,7 +27,7 @@
"empty": {
"welcome": {
"title": "Welcome to LifeForge!",
"description": "Get started by installing some modules from the ForgeStore."
"description": "Get started by installing some modules from Forgistry."
},
"notFound": {
"title": "Widget Not Found",

View File

@@ -3,7 +3,6 @@ import { useState } from 'react'
import { Button } from '@/components/inputs'
import { Box, Flex, Text } from '@/components/primitives'
import { TAILWIND_PALETTE } from '@/system'
import { TagsFilter } from './index'

View File

@@ -79,10 +79,10 @@ export function VirtualGrid<T>({
>
<Grid
as="div"
templateCols={`repeat(${itemsPerRow}, 1fr)`}
gap="md"
pb="md"
style={style}
templateCols={`repeat(${itemsPerRow}, 1fr)`}
width="100%"
>
{items.slice(fromIndex, toIndex).map(item => (

View File

@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
import { Button, Listbox, ListboxOption } from '@/components/inputs'
import { Box, Grid, Text } from '@/components/primitives'
import { ScrollableStory } from '@/storybook/ScrollableStory'
import { TAILWIND_PALETTE } from '@/system'
@@ -25,7 +24,7 @@ export const Default: Story = {
title: 'Cool Widget'
},
render: args => (
<Grid templateCols={3} gap="lg" templateRows={3}>
<Grid gap="lg" templateCols={3} templateRows={3}>
<Widget {...args}>
<Text as="p" color="bg-600">
This is a dashboard item.
@@ -63,7 +62,7 @@ export const WithDescription: Story = {
title: 'Cool Widget'
},
render: args => (
<Grid templateCols={3} gap="lg" templateRows={3}>
<Grid gap="lg" templateCols={3} templateRows={3}>
<Widget {...args}>
<Text as="p" color="bg-600">
This is a dashboard item.
@@ -101,7 +100,7 @@ export const WithIconColor: Story = {
},
render: args => (
<ScrollableStory>
<Grid templateCols={3} gap="md">
<Grid gap="md" templateCols={3}>
{[
TAILWIND_PALETTE.red[500],
TAILWIND_PALETTE.blue[500],
@@ -130,7 +129,7 @@ export const WithActionComponent: Story = {
title: 'A Cool Widget'
},
render: args => (
<Grid templateCols={2} gap="lg" templateRows={3}>
<Grid gap="lg" templateCols={2} templateRows={3}>
<Widget {...args}>
<Text as="p" color="bg-600">
This widget has a plus button beside the title. When clicked, maybe a
@@ -204,7 +203,7 @@ export const LargeIconWithIconColor: Story = {
},
render: args => (
<ScrollableStory>
<Grid templateCols={2} gap="md">
<Grid gap="md" templateCols={2}>
{[
TAILWIND_PALETTE.red[500],
TAILWIND_PALETTE.blue[500],

View File

@@ -1,8 +1,7 @@
import clsx from 'clsx'
import React from 'react'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
import * as styles from './Alert.css'

View File

@@ -1,8 +1,7 @@
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
interface EmptyStateScreenProps {
/** Props for the call-to-action button. Refer to the Button component for available props. */
@@ -71,6 +70,7 @@ export function EmptyStateScreen({
<Text
align="center"
as="h2"
color={{ base: 'bg-800', dark: 'bg-100' }}
size={smaller ? '2xl' : '3xl'}
style={{ paddingLeft: '1.5rem', paddingRight: '1.5rem' }}
weight="semibold"

View File

@@ -3,8 +3,7 @@ import clsx from 'clsx'
import { usePersonalization } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Bordered, Flex, Text, Transition } from '@/components/primitives'
import { Bordered, Flex, Icon, Text, Transition } from '@/components/primitives'
import { checkboxRootRecipe } from './Checkbox.css'

View File

@@ -2,8 +2,7 @@ import { usePersonalization } from '@lifeforge/shared'
import { Card } from '@/components/layout'
import { ModalHeader } from '@/components/overlays'
import { Icon } from '@/components/primitives'
import { Box, Flex, Grid, Text } from '@/components/primitives'
import { Box, Flex, Grid, Icon, Text } from '@/components/primitives'
import PALETTES from './constants/palettes.json'
@@ -27,11 +26,11 @@ function FlatUIColorsModal({
onClose={onClose}
/>
<Grid
style={{ gap: '0.75rem' }}
templateCols={{
base: 1,
sm: 'repeat(auto-fill, minmax(300px, 1fr))'
}}
style={{ gap: '0.75rem' }}
>
{PALETTES.map(({ name, icon, colors }) => (
<Card key={name} bg={{ base: 'bg-100', dark: 'bg-800' }}>
@@ -44,7 +43,7 @@ function FlatUIColorsModal({
{name}
</Text>
</Flex>
<Grid templateCols={5} style={{ gap: '0.5rem' }}>
<Grid style={{ gap: '0.5rem' }} templateCols={5}>
{colors.map((flatUiColor, index) => (
<Flex
key={index}

View File

@@ -4,8 +4,7 @@ import { sortFn } from 'color-sorter'
import { usePersonalization } from '@lifeforge/shared'
import { ModalHeader } from '@/components/overlays'
import { Icon } from '@/components/primitives'
import { Box, Flex, Grid } from '@/components/primitives'
import { Box, Flex, Grid, Icon } from '@/components/primitives'
import * as styles from './MorandiColorPaletteModal.css'
import { MORANDI_COLORS } from './constants/morandi_colors'
@@ -30,10 +29,10 @@ function MorandiColorPaletteModal({
onClose={onClose}
/>
<Grid
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
pb="md"
px="md"
style={{ gap: '0.75rem' }}
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
>
{MORANDI_COLORS.sort(sortFn).map((morandiColor, index) => (
<Flex key={index} asChild align="center" justify="center" r="md">

View File

@@ -4,8 +4,7 @@ import { memo, useMemo } from 'react'
import { usePersonalization } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import * as styles from './ColorItem.css'

View File

@@ -1,6 +1,5 @@
import { ModalHeader } from '@/components/overlays'
import { Box, Flex, Grid, Text } from '@/components/primitives'
import { TAILWIND_PALETTE } from '@/system'
import * as styles from './TailwindCSSColorsModal.css'
@@ -53,8 +52,8 @@ function TailwindCSSColorsModal({
<Grid
key={index}
as="ul"
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
style={{ gap: '0.75rem' }}
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
width="100%"
>
{Object.entries(

View File

@@ -1,10 +1,10 @@
import { ComboboxOption as HeadlessComboboxOption } from '@headlessui/react'
import { Icon } from '@/components/primitives'
import {
Bordered,
Box,
Flex,
Icon,
Text,
Transition,
WithDivide

View File

@@ -4,8 +4,7 @@ import {
} from '@headlessui/react'
import { useCallback, useMemo } from 'react'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { InputActionButton } from '../shared/components/InputActionButton'
import { InputIcon } from '../shared/components/InputIcon'

View File

@@ -1,6 +1,5 @@
import { TextInput } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Box, Text } from '@/components/primitives'
import { Box, Icon, Text } from '@/components/primitives'
export function ImageURL({
file,

View File

@@ -4,8 +4,7 @@ import type { DropzoneInputProps, DropzoneRootProps } from 'react-dropzone'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
import * as styles from './DnDContainer.css'

View File

@@ -1,8 +1,7 @@
import { useMemo } from 'react'
import { Button } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { FILE_ICONS } from '../../../constants/file_icons'

View File

@@ -1,6 +1,5 @@
import { ListboxInput, ListboxOption } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Text } from '@/components/primitives'
import { Icon, Text } from '@/components/primitives'
import {
type IPixabaySearchFilter,
@@ -26,9 +25,9 @@ export function CategoryFilter({
style={{ height: '1.25rem', width: '1.25rem' }}
/>
<Text
truncate
as="span"
style={{ display: 'block', marginTop: '-1px' }}
truncate
>
{CATEGORIES.find(l => l.id === category)?.name ?? 'None'}
</Text>

View File

@@ -30,9 +30,9 @@ export function ColorFilter({ colors, updateFilters }: ColorFilterProps) {
<div />
</Bordered>
<Text
truncate
as="span"
style={{ display: 'block', marginTop: '-1px' }}
truncate
>
{COLORS.find(l => l.id === colors)?.name ?? 'None'}
</Text>

View File

@@ -1,8 +1,7 @@
import { useTranslation } from 'react-i18next'
import { Switch } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
import { type PixabaySearchFilterAction } from '../../../typescript/pixabay_interfaces'

View File

@@ -1,8 +1,7 @@
import { useTranslation } from 'react-i18next'
import { ListboxInput, ListboxOption } from '@/components/inputs'
import { Icon } from '@/components/primitives'
import { Text } from '@/components/primitives'
import { Icon, Text } from '@/components/primitives'
import {
type IPixabaySearchFilter,
@@ -30,9 +29,9 @@ export function ImageTypeFilter({
style={{ height: '1.25rem', width: '1.25rem' }}
/>
<Text
truncate
as="span"
style={{ display: 'block', marginTop: '-1px' }}
truncate
>
{t(
`imagePicker.imageType.${IMAGE_TYPES.find(l => l.id === imageType)?.id}`

View File

@@ -11,7 +11,6 @@ import {
import { Button, SearchInput } from '@/components/inputs'
import { Flex } from '@/components/primitives'
import { WithQueryData } from '@/components/utilities'
import { forgeAPI } from '@/utils/forgeAPI'
import { SearchFilterModal } from './components/SearchFilterModal'

View File

@@ -3,8 +3,7 @@ import { useCallback, useMemo, useState } from 'react'
import { usePersonalization } from '@lifeforge/shared'
import { TagChip } from '@/components/data-display'
import { Icon } from '@/components/primitives'
import { Flex, Text, Transition } from '@/components/primitives'
import { Flex, Icon, Text, Transition } from '@/components/primitives'
export function ChipSelector({
options,

View File

@@ -2,8 +2,7 @@ import { useCallback, useState } from 'react'
import { GoBackButton } from '@/components/navigation'
import { ModalHeader } from '@/components/overlays'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text, Transition } from '@/components/primitives'
import { Box, Flex, Icon, Text, Transition } from '@/components/primitives'
import { IconSet } from './pages/IconSet'
import { IconSetList } from './pages/IconSetList/index'

View File

@@ -40,11 +40,11 @@ function _CategoryEntry({
</Text>
</Box>
<Grid
gap="sm"
templateCols={{
base: '1',
sm: 'repeat(auto-fill, minmax(320px, 1fr))'
}}
gap="sm"
>
{iconSets.map(iconSet => (
<IconSetEntry

View File

@@ -3,7 +3,12 @@ import { collections as importedCollections } from '@iconify/collections'
import { type IconifyInfo } from '@iconify/types'
import { memo, useCallback, useMemo } from 'react'
import { Button, Listbox, ListboxOption, SearchInput } from '@/components/inputs'
import {
Button,
Listbox,
ListboxOption,
SearchInput
} from '@/components/inputs'
import { Box, Flex } from '@/components/primitives'
const collections: Record<string, IconifyInfo> = importedCollections

View File

@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
import { useState } from 'react'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { COLORS, TAILWIND_PALETTE } from '@/system'
import { ListboxNullOption } from './components/ListboxNullOption'

View File

@@ -1,9 +1,9 @@
import { ListboxOption as HeadlessListboxOption } from '@headlessui/react'
import { Icon } from '@/components/primitives'
import {
Box,
Flex,
Icon,
Text,
Transition,
WithDivide

View File

@@ -1,11 +1,11 @@
import { ListboxOption as HeadlessListboxOption } from '@headlessui/react'
import { formatHex, parse } from 'culori'
import { Icon } from '@/components/primitives'
import {
Bordered,
Box,
Flex,
Icon,
Text,
Transition,
WithDivide

View File

@@ -7,7 +7,6 @@ import { useAPIEndpoint } from '@lifeforge/shared'
import { ComboboxInput, ComboboxOption } from '@/components/inputs'
import type { InputVariants } from '@/components/inputs/shared/types'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { forgeAPI } from '@/utils/forgeAPI'
import { useInputLabel } from '../shared/hooks/useInputLabel'

View File

@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
import { useState } from 'react'
import { Text } from '@/components/primitives'
import { ScrollableStory } from '@/storybook/ScrollableStory'
import { RRuleInput } from './index'

View File

@@ -2,8 +2,7 @@ import type { StoryObj, Meta as _Meta } from '@storybook/react-vite'
import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { WithQuery } from '@/components/utilities'
import { SearchInput } from './index'

View File

@@ -1,6 +1,5 @@
import { useInputLabel } from '@/components/inputs/shared/hooks/useInputLabel'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
export function SliderHeader({
icon,

View File

@@ -57,13 +57,13 @@ export function SliderInput({
>
<Box className={styles.fill} style={{ width: `${progress}%` }} />
<input
className={styles.input}
disabled={disabled}
max={max}
min={min}
step={step}
type="range"
value={value}
className={styles.input}
onChange={e => {
onChange(parseFloat(e.target.value))
}}

View File

@@ -1,7 +1,6 @@
import { type ComponentPropsWithoutRef, type ReactNode } from 'react'
import { Icon } from '@/components/primitives'
import { Flex, Text, Transition } from '@/components/primitives'
import { Flex, Icon, Text, Transition } from '@/components/primitives'
import type { InputVariant } from '../types'
@@ -42,8 +41,8 @@ export function InputActionButton({
justify="center"
p="sm"
position="absolute"
right={hasError ? (variant === 'classic' ? '3em' : '2.5em') : '0'}
r="lg"
right={hasError ? (variant === 'classic' ? '3em' : '2.5em') : '0'}
style={{
// the `mr` props cannot be used since the 0.75rem value is required to be exact for the plain variant
marginRight: variant === 'classic' ? '1em' : '0.75em',

View File

@@ -1,8 +1,7 @@
import clsx from 'clsx'
import { type CSSProperties, memo } from 'react'
import { Icon } from '@/components/primitives'
import { Flex, Text, Transition } from '@/components/primitives'
import { Flex, Icon, Text, Transition } from '@/components/primitives'
import { useInputFocused } from '../../contexts/InputFocusContext'
import {

View File

@@ -36,9 +36,9 @@ export function InputFocusProvider({
return (
<span
ref={containerRef as React.Ref<HTMLSpanElement>}
style={{ display: 'contents' }}
onBlur={handleBlur}
onFocus={handleFocus}
style={{ display: 'contents' }}
>
<InputFocusContext.Provider value={isFocused}>
{children}

View File

@@ -66,9 +66,9 @@ export const Default: Story = {
<ModuleHeader />
<Grid
templateCols="repeat(auto-fit, minmax(200px, 1fr))"
gap="md"
mb="xl"
templateCols="repeat(auto-fit, minmax(200px, 1fr))"
width="100%"
>
<StatCard label="Total Items" value="1,284" />

View File

@@ -1,7 +1,6 @@
import React from 'react'
import { Icon } from '@/components/primitives'
import { Text } from '@/components/primitives'
import { Icon, Text } from '@/components/primitives'
export function EllipsisIcon() {
return (

View File

@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
import { useState } from 'react'
import { Box } from '@/components/primitives'
import { TAILWIND_PALETTE } from '@/system'
import { Tabs } from '../Tabs'

View File

@@ -4,7 +4,6 @@ import { BrowserRouter } from 'react-router'
import { ContextMenuItem } from '@/components/overlays'
import { Flex, Text } from '@/components/primitives'
import { TAILWIND_PALETTE } from '@/system'
import { SidebarWrapper } from '../SidebarWrapper'

View File

@@ -1,7 +1,6 @@
import { useModuleSidebarState } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Box, Transition } from '@/components/primitives'
import { Box, Icon, Transition } from '@/components/primitives'
import * as styles from './SidebarActionButton.css'

View File

@@ -1,7 +1,6 @@
import { useModuleSidebarState } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Box, Text } from '@/components/primitives'
import { Box, Icon, Text } from '@/components/primitives'
export function SidebarCancelButton({ onClick }: { onClick: () => void }) {
const { setIsSidebarOpen } = useModuleSidebarState()

View File

@@ -1,5 +1,4 @@
import { Icon } from '@/components/primitives'
import { Bordered, Text } from '@/components/primitives'
import { Bordered, Icon, Text } from '@/components/primitives'
export function SidebarItemIcon({
icon,

View File

@@ -1,5 +1,4 @@
import { Icon } from '@/components/primitives'
import { Box, Text, Transition } from '@/components/primitives'
import { Box, Icon, Text, Transition } from '@/components/primitives'
export function SidebarItemSubsectionExpandIcon({
toggleSubsection,

View File

@@ -3,11 +3,9 @@ import _ from 'lodash'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, useLocation } from '@lifeforge/shared'
import { useMainSidebarState } from '@lifeforge/shared'
import { Link, useLocation, useMainSidebarState } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
import * as styles from './SidebarSubsectionItemLink.css'

View File

@@ -5,8 +5,7 @@ import { useTranslation } from 'react-i18next'
import { useModuleSidebarState } from '@lifeforge/shared'
import { Icon } from '@/components/primitives'
import { Flex, Text } from '@/components/primitives'
import { Flex, Icon, Text } from '@/components/primitives'
import * as styles from './SidebarSubsectionItemWithOnClick.css'

View File

@@ -1,7 +1,6 @@
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import * as styles from './ContextMenuGroup.css'

View File

@@ -4,7 +4,6 @@ import _ from 'lodash'
import { useTranslation } from 'react-i18next'
import { Flex, Icon, Text } from '@/components/primitives'
import { vars } from '@/system'
import * as styles from './ContextMenuItem.css'

View File

@@ -291,8 +291,8 @@ export const Positioning: Story = {
bottom="0"
p="xs"
position="absolute"
right="0"
r="md"
right="0"
style={{ margin: '0.5rem' }}
>
<PlaceholderContent

View File

@@ -6,7 +6,7 @@ export function EncryptionWrapper({ children }: { children: React.ReactNode }) {
const { ready, error } = useEncryption()
if (!ready) {
return <LoadingScreen message="Checking API status..." />
return <LoadingScreen message="Initializing end-to-end encryption..." />
}
if (error) {

View File

@@ -1,7 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Icon } from '@/components/primitives'
import { Box, Flex, Text } from '@/components/primitives'
import { Box, Flex, Icon, Text } from '@/components/primitives'
import { Tooltip } from './index'

View File

@@ -29,7 +29,11 @@ const RESPONSIVE_PROPS = [
{ className: 'lf-gtr', property: 'gridTemplateRows', customProp: '--lf-gtr' },
{ className: 'lf-zi', property: 'zIndex', customProp: '--lf-zi' },
{ className: 'lf-rw', property: '--lf-ring-width', customProp: '--lf-rw' },
{ className: 'lf-row', property: '--lf-ring-offset-width', customProp: '--lf-row' }
{
className: 'lf-row',
property: '--lf-ring-offset-width',
customProp: '--lf-row'
}
] as const
// Breakpoint media queries

View File

@@ -4,18 +4,10 @@
"jsx": "react-jsx",
"module": "esnext",
"moduleResolution": "bundler",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"paths": {
"@/components/*": [
"./src/components/*"
],
"@/*": [
"./src/*"
]
"@/components/*": ["./src/components/*"],
"@/*": ["./src/*"]
},
"declaration": true,
"declarationMap": true,
@@ -28,8 +20,5 @@
"skipLibCheck": true,
"verbatimModuleSyntax": true
},
"include": [
"./src/**/*",
"./.storybook/*"
]
}
"include": ["./src/**/*", "./.storybook/*"]
}

View File

@@ -1,9 +1,8 @@
import { createServiceLogger } from '@functions/logging'
import PocketBase from 'pocketbase'
import { CleanedSchemas, IPBService } from '@lifeforge/server-utils'
import { createServiceLogger } from '@functions/logging'
import create from './services/create'
import deleteRecord from './services/delete'
import getFirstListItem from './services/getFirstListItem'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -11,8 +12,6 @@ import {
ICreateFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -9,8 +10,6 @@ import {
IDeleteFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -12,8 +13,6 @@ import {
IGetFirstListItemFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -12,8 +13,6 @@ import {
IGetFullListFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -13,8 +14,6 @@ import {
IGetListReturnType
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -10,8 +11,6 @@ import {
IGetOneFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'

View File

@@ -1,3 +1,4 @@
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import chalk from 'chalk'
import PocketBase from 'pocketbase'
@@ -11,8 +12,6 @@ import {
IUpdateFactory
} from '@lifeforge/server-utils'
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
import { PBLogger } from '..'
import getFinalCollectionName from '../utils/getFinalCollectionName'

View File

@@ -1,13 +1,12 @@
import { ROOT_DIR } from '@constants'
import { decrypt2 } from '@functions/auth/encryption'
import { createServiceLogger } from '@functions/logging'
import chalk from 'chalk'
import fs from 'fs'
import path from 'path'
import { IPBService } from '@lifeforge/server-utils'
import { decrypt2 } from '@functions/auth/encryption'
import { createServiceLogger } from '@functions/logging'
import PBService from './PBService'
const logger = createServiceLogger('API Key Vault')

View File

@@ -1,7 +1,6 @@
import { ROOT_DIR } from '@constants'
import path from 'path'
import { createServiceLogger } from '@functions/logging'
import path from 'path'
// Key storage paths
export const KEYS_DIR = path.join(ROOT_DIR, 'keys')

View File

@@ -1,3 +1,7 @@
import { getAPIKey } from '@functions/database'
import { validateCallerAccess } from '@functions/database/getAPIKey'
import { createServiceLogger } from '@functions/logging'
import { zodTextFormat } from '@functions/utils/zodResponseFormat'
import chalk from 'chalk'
import Groq from 'groq-sdk'
import { ChatCompletionMessageParam as GroqChatCompletionMessageParam } from 'groq-sdk/resources/chat/completions.mjs'
@@ -10,11 +14,6 @@ import {
getCallerModuleId
} from '@lifeforge/server-utils'
import { getAPIKey } from '@functions/database'
import { validateCallerAccess } from '@functions/database/getAPIKey'
import { createServiceLogger } from '@functions/logging'
import { zodTextFormat } from '@functions/utils/zodResponseFormat'
const logger = createServiceLogger('AI')
const fetchAI: FetchAIFunc = async ({

View File

@@ -1,9 +1,8 @@
import { ROOT_DIR } from '@constants'
import dotenv from 'dotenv'
import path from 'path'
import { ensureKeysExist } from '@functions/encryption'
import { coreLogger } from '@functions/logging'
import dotenv from 'dotenv'
import path from 'path'
export default function ensureCredentials(): void {
dotenv.config({

View File

@@ -1,11 +1,10 @@
import { ROOT_DIR } from '@constants'
import { createServiceLogger } from '@functions/logging'
import chalk from 'chalk'
import fs from 'fs'
import path from 'path'
import z from 'zod'
import { createServiceLogger } from '@functions/logging'
const localeLogger = createServiceLogger('Locale')
export const ALLOWED_NAMESPACE = ['apps', 'common'] as const

View File

@@ -1,11 +1,10 @@
import { ROOT_DIR } from '@constants'
import { createServiceLogger } from '@functions/logging'
import chalk from 'chalk'
import fs from 'fs'
import _ from 'lodash'
import path from 'path'
import { createServiceLogger } from '@functions/logging'
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
const logger = createServiceLogger('Route Loader')

View File

@@ -13,6 +13,8 @@
* The main export is:
* - `registerController`: Function to register a ForgeControllerBuilder with an Express router
*/
import { encryptResponse } from '@functions/encryption'
import { coreLogger } from '@functions/logging'
import type { Request, Response, Router } from 'express'
import {
@@ -24,9 +26,6 @@ import {
MediaConfig
} from '@lifeforge/server-utils'
import { encryptResponse } from '@functions/encryption'
import { coreLogger } from '@functions/logging'
import checkRecordExistence from '../utils/checkRecordExistence'
import { createCoreContext } from '../utils/coreContext'
import getAESKey from '../utils/getAESKey'
@@ -89,7 +88,6 @@ function createHandler<
const aesKey = getAESKey(req, res, encrypted, callerModuleId)
try {
parseQuery(req, schema.query)

View File

@@ -1,10 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { PBService, checkExistence } from '@functions/database'
import { Request } from 'express'
import { ClientError } from '@lifeforge/server-utils'
import { PBService, checkExistence } from '@functions/database'
async function check(
pb: PBService<any>,
collection: string,

View File

@@ -4,9 +4,6 @@
* This module provides the implementation of core utilities that modules
* access via the `core` parameter in their callbacks.
*/
import { type Logger, createLogger } from '@lifeforge/log'
import { CoreContext, IPBService } from '@lifeforge/server-utils'
import {
decrypt,
decrypt2,
@@ -28,6 +25,9 @@ import {
import { checkModulesAvailability } from '@functions/utils/checkModulesAvailability'
import TempFileManager from '@functions/utils/tempFileManager'
import { type Logger, createLogger } from '@lifeforge/log'
import { CoreContext, IPBService } from '@lifeforge/server-utils'
// Cache loggers per module to avoid creating new file stream listeners per request
const loggerCache = new Map<string, Logger>()

View File

@@ -1,6 +1,5 @@
import { Request, Response } from 'express'
import { decryptAESKey } from '@functions/encryption'
import { Request, Response } from 'express'
import { clientError } from './response'

View File

@@ -1,9 +1,8 @@
import { coreLogger } from '@functions/logging'
import chalk from 'chalk'
import { MediaConfig } from '@lifeforge/server-utils'
import { coreLogger } from '@functions/logging'
type MediaResponse = Record<
string,
Express.Multer.File | Express.Multer.File[] | undefined

View File

@@ -1,8 +1,7 @@
import { PBService } from '@functions/database'
import { Request, Response } from 'express'
import Pocketbase from 'pocketbase'
import { PBService } from '@functions/database'
export default async function isAuthTokenValid(
req: Request<unknown, unknown, unknown, unknown>,
res: Response,

View File

@@ -1,8 +1,7 @@
import { globalTaskPool } from '@functions/socketio/taskPool'
import Pocketbase from 'pocketbase'
import { Server } from 'socket.io'
import { globalTaskPool } from '@functions/socketio/taskPool'
export function setupSocket(io: Server) {
// QR Login namespace - no authentication required
// This allows unauthenticated desktop clients to listen for login approval

View File

@@ -1,9 +1,8 @@
import { coreLogger } from '@functions/logging'
import chalk from 'chalk'
import { Request, Response } from 'express'
import morgan from 'morgan'
import { coreLogger } from '@functions/logging'
const METHOD_COLOR = {
GET: '#34ace0',
POST: '#2ed573'

View File

@@ -103,5 +103,4 @@ const coreRoutes = forgeRouter({
encryptionPublicKey
})
export default coreRoutes

View File

@@ -1,13 +1,12 @@
import { ROOT_DIR } from '@constants'
import express from 'express'
import path from 'path'
import { forgeRouter } from '@lifeforge/server-utils'
import traceRouteStack from '@functions/initialization/traceRouteStack'
import { loadModuleRoutes } from '@functions/modules/loadModuleRoutes'
import { registerRoutes } from '@functions/routes/functions/forgeRouter'
import { clientError } from '@functions/routes/utils/response'
import express from 'express'
import path from 'path'
import { forgeRouter } from '@lifeforge/server-utils'
import coreRoutes from './core.routes'
import forge from './forge'

View File

@@ -1,15 +1,14 @@
import { PORT } from '@constants'
import chalk from 'chalk'
import { program } from 'commander'
import fs from 'fs'
import { createServer } from 'node:http'
import checkDB from '@functions/database/dbUtils'
import ensureCredentials from '@functions/initialization/ensureCredentials'
import { LocaleService } from '@functions/initialization/localeService'
import traceRouteStack from '@functions/initialization/traceRouteStack'
import { LOG_LEVELS, type LogLevel, coreLogger } from '@functions/logging'
import createSocketServer from '@functions/socketio/createSocketServer'
import chalk from 'chalk'
import { program } from 'commander'
import fs from 'fs'
import { createServer } from 'node:http'
import app from './core/app'

View File

@@ -1,12 +1,11 @@
import dayjs from 'dayjs'
import z from 'zod'
import { createForge, forgeRouter } from '@lifeforge/server-utils'
import {
connectToPocketBase,
validateEnvironmentVariables
} from '@functions/database/dbUtils'
import dayjs from 'dayjs'
import z from 'zod'
import { createForge, forgeRouter } from '@lifeforge/server-utils'
const forge = createForge({}, 'backups')

View File

@@ -1,10 +1,10 @@
import { createForge, forgeRouter } from '@lifeforge/server-utils'
import {
connectToPocketBase,
validateEnvironmentVariables
} from '@functions/database/dbUtils'
import { createForge, forgeRouter } from '@lifeforge/server-utils'
const forge = createForge({}, 'database')
const list = forge

View File

@@ -1,4 +1,8 @@
import { ROOT_DIR } from '@constants'
import {
ALLOWED_NAMESPACE,
LocaleService
} from '@functions/initialization/localeService'
import chalk from 'chalk'
import fs from 'fs'
import path from 'path'
@@ -7,11 +11,6 @@ import z from 'zod'
import { ClientError, createForge, forgeRouter } from '@lifeforge/server-utils'
import { normalizeSubnamespace } from '@lifeforge/shared'
import {
ALLOWED_NAMESPACE,
LocaleService
} from '@functions/initialization/localeService'
const forge = createForge({}, 'locales')
// Scan apps directory for modules with locales

Some files were not shown because too many files have changed in this diff Show More