create-yiougo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +120 -0
  2. package/index.js +76 -0
  3. package/package.json +45 -0
  4. package/template/.env.example +7 -0
  5. package/template/README.md +122 -0
  6. package/template/index.html +13 -0
  7. package/template/package-lock.json +3543 -0
  8. package/template/package.json +38 -0
  9. package/template/public/vite.svg +1 -0
  10. package/template/server/config/database.ts +31 -0
  11. package/template/server/config/redis.ts +23 -0
  12. package/template/server/index.ts +28 -0
  13. package/template/server/models/user.model.ts +10 -0
  14. package/template/server/routes/user.routes.ts +45 -0
  15. package/template/server/services/user.service.ts +100 -0
  16. package/template/shared/types/index.ts +14 -0
  17. package/template/src/App.vue +16 -0
  18. package/template/src/api/user.api.ts +53 -0
  19. package/template/src/components/BaseButton.vue +49 -0
  20. package/template/src/components/BaseCard.vue +41 -0
  21. package/template/src/components/index.ts +3 -0
  22. package/template/src/composables/index.ts +3 -0
  23. package/template/src/composables/useLoading.ts +36 -0
  24. package/template/src/composables/useNotification.ts +52 -0
  25. package/template/src/env.d.ts +13 -0
  26. package/template/src/hooks/index.ts +2 -0
  27. package/template/src/hooks/useApi.ts +60 -0
  28. package/template/src/layouts/default.vue +29 -0
  29. package/template/src/main.ts +20 -0
  30. package/template/src/pages/about.vue +111 -0
  31. package/template/src/pages/index.vue +38 -0
  32. package/template/src/pages/users.vue +126 -0
  33. package/template/src/router/index.ts +12 -0
  34. package/template/src/stores/user.store.ts +64 -0
  35. package/template/src/styles/main.scss +15 -0
  36. package/template/src/styles/quasar-variables.sass +8 -0
  37. package/template/src/types/global.d.ts +28 -0
  38. package/template/src/utils/date.ts +49 -0
  39. package/template/src/utils/index.ts +3 -0
  40. package/template/src/utils/storage.ts +56 -0
  41. package/template/src/vite-env.d.ts +7 -0
  42. package/template/tsconfig.app.json +32 -0
  43. package/template/tsconfig.json +7 -0
  44. package/template/tsconfig.node.json +23 -0
  45. package/template/tsconfig.server.json +29 -0
  46. package/template/vite.config.ts +42 -0
@@ -0,0 +1,20 @@
1
+ import { createApp } from 'vue';
2
+ import { createPinia } from 'pinia';
3
+ import { Quasar } from 'quasar';
4
+ import router from './router';
5
+ import App from './App.vue';
6
+
7
+ // import layouts from 'virtual:generated-layouts' // vite-plugin-vue-layouts 自动处理
8
+
9
+ import '@quasar/extras/material-icons/material-icons.css';
10
+ import 'quasar/src/css/index.sass';
11
+ import './styles/main.scss';
12
+
13
+ const app = createApp(App);
14
+ const pinia = createPinia();
15
+
16
+ app.use(pinia);
17
+ app.use(router);
18
+ app.use(Quasar, {});
19
+
20
+ app.mount('#app');
@@ -0,0 +1,111 @@
1
+ <template>
2
+ <q-page class="q-pa-md">
3
+ <q-card>
4
+ <q-card-section>
5
+ <div class="text-h4 q-mb-md">About Yiougo</div>
6
+ <p class="text-body1">
7
+ A modern full-stack application template built with cutting-edge technologies.
8
+ </p>
9
+ </q-card-section>
10
+
11
+ <q-separator />
12
+
13
+ <q-card-section>
14
+ <div class="text-h6 q-mb-md">Backend Stack</div>
15
+ <q-list>
16
+ <q-item>
17
+ <q-item-section avatar>
18
+ <q-icon color="primary" name="check_circle" />
19
+ </q-item-section>
20
+ <q-item-section>
21
+ <q-item-label>Elysia</q-item-label>
22
+ <q-item-label caption>Fast and ergonomic web framework for Bun</q-item-label>
23
+ </q-item-section>
24
+ </q-item>
25
+ <q-item>
26
+ <q-item-section avatar>
27
+ <q-icon color="primary" name="check_circle" />
28
+ </q-item-section>
29
+ <q-item-section>
30
+ <q-item-label>MongoDB</q-item-label>
31
+ <q-item-label caption>NoSQL database</q-item-label>
32
+ </q-item-section>
33
+ </q-item>
34
+ <q-item>
35
+ <q-item-section avatar>
36
+ <q-icon color="primary" name="check_circle" />
37
+ </q-item-section>
38
+ <q-item-section>
39
+ <q-item-label>Redis</q-item-label>
40
+ <q-item-label caption>In-memory data store for caching</q-item-label>
41
+ </q-item-section>
42
+ </q-item>
43
+ </q-list>
44
+ </q-card-section>
45
+
46
+ <q-separator />
47
+
48
+ <q-card-section>
49
+ <div class="text-h6 q-mb-md">Frontend Stack</div>
50
+ <q-list>
51
+ <q-item>
52
+ <q-item-section avatar>
53
+ <q-icon color="secondary" name="check_circle" />
54
+ </q-item-section>
55
+ <q-item-section>
56
+ <q-item-label>Vue 3</q-item-label>
57
+ <q-item-label caption>Progressive JavaScript framework</q-item-label>
58
+ </q-item-section>
59
+ </q-item>
60
+ <q-item>
61
+ <q-item-section avatar>
62
+ <q-icon color="secondary" name="check_circle" />
63
+ </q-item-section>
64
+ <q-item-section>
65
+ <q-item-label>Vite</q-item-label>
66
+ <q-item-label caption>Next generation frontend tooling</q-item-label>
67
+ </q-item-section>
68
+ </q-item>
69
+ <q-item>
70
+ <q-item-section avatar>
71
+ <q-icon color="secondary" name="check_circle" />
72
+ </q-item-section>
73
+ <q-item-section>
74
+ <q-item-label>Quasar</q-item-label>
75
+ <q-item-label caption>Vue component framework</q-item-label>
76
+ </q-item-section>
77
+ </q-item>
78
+ <q-item>
79
+ <q-item-section avatar>
80
+ <q-icon color="secondary" name="check_circle" />
81
+ </q-item-section>
82
+ <q-item-section>
83
+ <q-item-label>Pinia</q-item-label>
84
+ <q-item-label caption>Vue store</q-item-label>
85
+ </q-item-section>
86
+ </q-item>
87
+ <q-item>
88
+ <q-item-section avatar>
89
+ <q-icon color="secondary" name="check_circle" />
90
+ </q-item-section>
91
+ <q-item-section>
92
+ <q-item-label>Vue Router</q-item-label>
93
+ <q-item-label caption>Official router for Vue.js</q-item-label>
94
+ </q-item-section>
95
+ </q-item>
96
+ </q-list>
97
+ </q-card-section>
98
+ </q-card>
99
+ </q-page>
100
+ </template>
101
+
102
+ <script setup lang="ts">
103
+ </script>
104
+
105
+ <route lang="yaml">
106
+ meta:
107
+ layout: default
108
+ </route>
109
+
110
+ <style lang="scss" scoped>
111
+ </style>
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <q-page class="flex flex-center">
3
+ <div class="text-center">
4
+ <h1 class="text-h2 text-primary q-mb-md">Welcome to Yiougo</h1>
5
+ <p class="text-h6 text-grey-7 q-mb-lg">
6
+ A modern full-stack application built with Elysia and Vue 3
7
+ </p>
8
+ <div class="q-gutter-md">
9
+ <q-btn
10
+ color="primary"
11
+ label="View Users"
12
+ icon="people"
13
+ to="/users"
14
+ size="lg"
15
+ />
16
+ <q-btn
17
+ color="secondary"
18
+ label="About"
19
+ icon="info"
20
+ to="/about"
21
+ size="lg"
22
+ outline
23
+ />
24
+ </div>
25
+ </div>
26
+ </q-page>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ </script>
31
+
32
+ <route lang="yaml">
33
+ meta:
34
+ layout: default
35
+ </route>
36
+
37
+ <style lang="scss" scoped>
38
+ </style>
@@ -0,0 +1,126 @@
1
+ <template>
2
+ <q-page class="q-pa-md">
3
+ <div class="row justify-between items-center q-mb-md">
4
+ <h4 class="q-ma-none">Users</h4>
5
+ <q-btn color="primary" label="Add User" icon="add" @click="showDialog = true" />
6
+ </div>
7
+
8
+ <q-card>
9
+ <q-card-section>
10
+ <q-table
11
+ :rows="userStore.users"
12
+ :columns="columns"
13
+ row-key="_id"
14
+ :loading="userStore.loading"
15
+ flat
16
+ bordered
17
+ >
18
+ <template v-slot:body-cell-actions="props">
19
+ <q-td :props="props">
20
+ <q-btn
21
+ flat
22
+ round
23
+ dense
24
+ color="negative"
25
+ icon="delete"
26
+ @click="handleDelete(props.row._id)"
27
+ />
28
+ </q-td>
29
+ </template>
30
+ </q-table>
31
+ </q-card-section>
32
+ </q-card>
33
+
34
+ <q-dialog v-model="showDialog">
35
+ <q-card style="min-width: 400px">
36
+ <q-card-section>
37
+ <div class="text-h6">Add New User</div>
38
+ </q-card-section>
39
+
40
+ <q-card-section class="q-pt-none">
41
+ <q-input v-model="newUser.username" label="Username" />
42
+ <q-input v-model="newUser.email" label="Email" type="email" />
43
+ <q-input v-model="newUser.password" label="Password" type="password" />
44
+ </q-card-section>
45
+
46
+ <q-card-actions align="right">
47
+ <q-btn flat label="Cancel" color="primary" v-close-popup />
48
+ <q-btn label="Create" color="primary" @click="handleCreate" />
49
+ </q-card-actions>
50
+ </q-card>
51
+ </q-dialog>
52
+ </q-page>
53
+ </template>
54
+
55
+ <script setup lang="ts">
56
+ import { ref, onMounted } from 'vue';
57
+ import { useUserStore } from '@/stores/user.store';
58
+ import { useQuasar } from 'quasar';
59
+
60
+ const userStore = useUserStore();
61
+ const $q = useQuasar();
62
+ const showDialog = ref(false);
63
+ const newUser = ref({
64
+ username: '',
65
+ email: '',
66
+ password: ''
67
+ });
68
+
69
+ const columns = [
70
+ { name: 'username', label: 'Username', field: 'username', align: 'left' as const },
71
+ { name: 'email', label: 'Email', field: 'email', align: 'left' as const },
72
+ { name: 'createdAt', label: 'Created At', field: 'createdAt', align: 'left' as const },
73
+ { name: 'actions', label: 'Actions', field: 'actions', align: 'center' as const }
74
+ ];
75
+
76
+ onMounted(() => {
77
+ userStore.fetchUsers();
78
+ });
79
+
80
+ async function handleCreate() {
81
+ await userStore.createUser(newUser.value);
82
+ if (!userStore.error) {
83
+ showDialog.value = false;
84
+ newUser.value = { username: '', email: '', password: '' };
85
+ $q.notify({
86
+ type: 'positive',
87
+ message: 'User created successfully'
88
+ });
89
+ } else {
90
+ $q.notify({
91
+ type: 'negative',
92
+ message: userStore.error
93
+ });
94
+ }
95
+ }
96
+
97
+ async function handleDelete(id: string) {
98
+ $q.dialog({
99
+ title: 'Confirm',
100
+ message: 'Are you sure you want to delete this user?',
101
+ cancel: true,
102
+ persistent: true
103
+ }).onOk(async () => {
104
+ await userStore.deleteUser(id);
105
+ if (!userStore.error) {
106
+ $q.notify({
107
+ type: 'positive',
108
+ message: 'User deleted successfully'
109
+ });
110
+ } else {
111
+ $q.notify({
112
+ type: 'negative',
113
+ message: userStore.error
114
+ });
115
+ }
116
+ });
117
+ }
118
+ </script>
119
+
120
+ <route lang="yaml">
121
+ meta:
122
+ layout: default
123
+ </route>
124
+
125
+ <style lang="scss" scoped>
126
+ </style>
@@ -0,0 +1,12 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+ import { setupLayouts } from 'virtual:generated-layouts'
3
+ import generatedRoutes from '~pages'
4
+
5
+ const routes = setupLayouts(generatedRoutes)
6
+
7
+ const router = createRouter({
8
+ history: createWebHistory(import.meta.env.BASE_URL),
9
+ routes,
10
+ })
11
+
12
+ export default router;
@@ -0,0 +1,64 @@
1
+ import { defineStore } from 'pinia';
2
+ import { ref } from 'vue';
3
+ import type { User } from '@shared/types';
4
+ import { userApi } from '@/api/user.api';
5
+
6
+ export const useUserStore = defineStore('user', () => {
7
+ const users = ref<User[]>([]);
8
+ const loading = ref(false);
9
+ const error = ref<string | null>(null);
10
+
11
+ async function fetchUsers() {
12
+ loading.value = true;
13
+ error.value = null;
14
+ try {
15
+ const response = await userApi.getAll();
16
+ if (response.success) {
17
+ users.value = response.data;
18
+ }
19
+ } catch (err) {
20
+ error.value = err instanceof Error ? err.message : 'Failed to fetch users';
21
+ } finally {
22
+ loading.value = false;
23
+ }
24
+ }
25
+
26
+ async function createUser(userData: Partial<User>) {
27
+ loading.value = true;
28
+ error.value = null;
29
+ try {
30
+ const response = await userApi.create(userData);
31
+ if (response.success) {
32
+ users.value.push(response.data);
33
+ }
34
+ } catch (err) {
35
+ error.value = err instanceof Error ? err.message : 'Failed to create user';
36
+ } finally {
37
+ loading.value = false;
38
+ }
39
+ }
40
+
41
+ async function deleteUser(id: string) {
42
+ loading.value = true;
43
+ error.value = null;
44
+ try {
45
+ const response = await userApi.delete(id);
46
+ if (response.success) {
47
+ users.value = users.value.filter(u => u._id !== id);
48
+ }
49
+ } catch (err) {
50
+ error.value = err instanceof Error ? err.message : 'Failed to delete user';
51
+ } finally {
52
+ loading.value = false;
53
+ }
54
+ }
55
+
56
+ return {
57
+ users,
58
+ loading,
59
+ error,
60
+ fetchUsers,
61
+ createUser,
62
+ deleteUser
63
+ };
64
+ });
@@ -0,0 +1,15 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Roboto', sans-serif;
9
+ }
10
+
11
+ .flex-center {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ }
@@ -0,0 +1,8 @@
1
+ $primary: #1976D2
2
+ $secondary: #26A69A
3
+ $accent: #9C27B0
4
+ $dark: #1D1D1D
5
+ $positive: #21BA45
6
+ $negative: #C10015
7
+ $info: #31CCEC
8
+ $warning: #F2C037
@@ -0,0 +1,28 @@
1
+ // 全局类型声明
2
+ declare module '~pages' {
3
+ import type { RouteRecordRaw } from 'vue-router'
4
+ const routes: RouteRecordRaw[]
5
+ export default routes
6
+ }
7
+
8
+ declare module 'node:url' {
9
+ export * from 'url'
10
+ }
11
+
12
+ declare module 'vite-plugin-pages' {
13
+ export interface PagesOptions {
14
+ dirs?: string[]
15
+ extensions?: string[]
16
+ importMode?: 'sync' | 'async'
17
+ routeBlockLang?: string
18
+ }
19
+ export default function Pages(options?: PagesOptions): any
20
+ }
21
+
22
+ declare module 'vite-plugin-vue-layouts' {
23
+ export interface LayoutsOptions {
24
+ layoutsDirs?: string
25
+ defaultLayout?: string
26
+ }
27
+ export default function Layouts(options?: LayoutsOptions): any
28
+ }
@@ -0,0 +1,49 @@
1
+ export function formatDate(date: Date | string, format: string = 'YYYY-MM-DD'): string {
2
+ const d = new Date(date)
3
+
4
+ if (isNaN(d.getTime())) {
5
+ return ''
6
+ }
7
+
8
+ const year = d.getFullYear()
9
+ const month = String(d.getMonth() + 1).padStart(2, '0')
10
+ const day = String(d.getDate()).padStart(2, '0')
11
+ const hours = String(d.getHours()).padStart(2, '0')
12
+ const minutes = String(d.getMinutes()).padStart(2, '0')
13
+ const seconds = String(d.getSeconds()).padStart(2, '0')
14
+
15
+ return format
16
+ .replace('YYYY', String(year))
17
+ .replace('MM', month)
18
+ .replace('DD', day)
19
+ .replace('HH', hours)
20
+ .replace('mm', minutes)
21
+ .replace('ss', seconds)
22
+ }
23
+
24
+ export function timeAgo(date: Date | string): string {
25
+ const now = new Date()
26
+ const past = new Date(date)
27
+ const diffInSeconds = Math.floor((now.getTime() - past.getTime()) / 1000)
28
+
29
+ if (diffInSeconds < 60) {
30
+ return '刚刚'
31
+ }
32
+
33
+ const diffInMinutes = Math.floor(diffInSeconds / 60)
34
+ if (diffInMinutes < 60) {
35
+ return `${diffInMinutes}分钟前`
36
+ }
37
+
38
+ const diffInHours = Math.floor(diffInMinutes / 60)
39
+ if (diffInHours < 24) {
40
+ return `${diffInHours}小时前`
41
+ }
42
+
43
+ const diffInDays = Math.floor(diffInHours / 24)
44
+ if (diffInDays < 30) {
45
+ return `${diffInDays}天前`
46
+ }
47
+
48
+ return formatDate(past)
49
+ }
@@ -0,0 +1,3 @@
1
+ // 工具函数导出
2
+ export * from './date'
3
+ export * from './storage'
@@ -0,0 +1,56 @@
1
+ export function getStorage<T>(key: string, defaultValue?: T): T | null {
2
+ try {
3
+ const item = localStorage.getItem(key)
4
+ if (item === null) {
5
+ return defaultValue ?? null
6
+ }
7
+ return JSON.parse(item) as T
8
+ } catch {
9
+ return defaultValue ?? null
10
+ }
11
+ }
12
+
13
+ export function setStorage<T>(key: string, value: T): void {
14
+ try {
15
+ localStorage.setItem(key, JSON.stringify(value))
16
+ } catch (error) {
17
+ console.error('Failed to save to localStorage:', error)
18
+ }
19
+ }
20
+
21
+ export function removeStorage(key: string): void {
22
+ try {
23
+ localStorage.removeItem(key)
24
+ } catch (error) {
25
+ console.error('Failed to remove from localStorage:', error)
26
+ }
27
+ }
28
+
29
+ export function clearStorage(): void {
30
+ try {
31
+ localStorage.clear()
32
+ } catch (error) {
33
+ console.error('Failed to clear localStorage:', error)
34
+ }
35
+ }
36
+
37
+ // Session Storage
38
+ export function getSessionStorage<T>(key: string, defaultValue?: T): T | null {
39
+ try {
40
+ const item = sessionStorage.getItem(key)
41
+ if (item === null) {
42
+ return defaultValue ?? null
43
+ }
44
+ return JSON.parse(item) as T
45
+ } catch {
46
+ return defaultValue ?? null
47
+ }
48
+ }
49
+
50
+ export function setSessionStorage<T>(key: string, value: T): void {
51
+ try {
52
+ sessionStorage.setItem(key, JSON.stringify(value))
53
+ } catch (error) {
54
+ console.error('Failed to save to sessionStorage:', error)
55
+ }
56
+ }
@@ -0,0 +1,7 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module '*.vue' {
4
+ import type { DefineComponent } from 'vue'
5
+ const component: DefineComponent<{}, {}, any>
6
+ export default component
7
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "isolatedModules": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+ "jsx": "preserve",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedSideEffectImports": true,
23
+
24
+ "baseUrl": ".",
25
+ "paths": {
26
+ "@/*": ["./src/*"],
27
+ "@shared/*": ["./shared/*"]
28
+ },
29
+ "types": ["vite/client", "node"]
30
+ },
31
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "shared/**/*.ts"]
32
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+
8
+ /* Bundler mode */
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "isolatedModules": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+
15
+ /* Linting */
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "noUncheckedSideEffectImports": true
21
+ },
22
+ "include": ["vite.config.ts"]
23
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleDetection": "force",
7
+ "jsx": "react-jsx",
8
+ "allowJs": true,
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "verbatimModuleSyntax": true,
12
+ "noEmit": true,
13
+ "strict": true,
14
+ "skipLibCheck": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false,
18
+ "noPropertyAccessFromIndexSignature": false,
19
+ "esModuleInterop": true,
20
+ "resolveJsonModule": true,
21
+ "isolatedModules": true,
22
+ "baseUrl": ".",
23
+ "paths": {
24
+ "@shared/*": ["./shared/*"]
25
+ },
26
+ "types": ["bun-types"]
27
+ },
28
+ "include": ["server/**/*.ts", "shared/**/*.ts"]
29
+ }
@@ -0,0 +1,42 @@
1
+ import { fileURLToPath, URL } from 'node:url'
2
+
3
+ import { defineConfig } from 'vite'
4
+ import vue from '@vitejs/plugin-vue'
5
+ import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
6
+ import Pages from 'vite-plugin-pages'
7
+ import Layouts from 'vite-plugin-vue-layouts'
8
+
9
+ // https://vite.dev/config/
10
+ export default defineConfig({
11
+ plugins: [
12
+ vue({
13
+ template: { transformAssetUrls }
14
+ }),
15
+ Pages({
16
+ dirs: ['src/pages'],
17
+ extensions: ['vue'],
18
+ importMode: 'async',
19
+ routeBlockLang: 'yaml'
20
+ }),
21
+ Layouts(),
22
+ quasar({
23
+ sassVariables: fileURLToPath(new URL('./src/styles/quasar-variables.sass', import.meta.url))
24
+ })
25
+ ],
26
+ resolve: {
27
+ alias: {
28
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
29
+ '@shared': fileURLToPath(new URL('./shared', import.meta.url))
30
+ }
31
+ },
32
+ server: {
33
+ port: 5173,
34
+ host: '0.0.0.0',
35
+ proxy: {
36
+ '/api': {
37
+ target: 'http://localhost:3000',
38
+ changeOrigin: true
39
+ }
40
+ }
41
+ }
42
+ })