@hywax/cms 2.0.2 → 3.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.
- package/.nuxt/cms/form-panel-section.ts +2 -2
- package/.nuxt/cms/{input-uplora-image.ts → form-uplora-image.ts} +2 -9
- package/.nuxt/cms/http-statuses.ts +59 -0
- package/.nuxt/cms/index.ts +4 -4
- package/.nuxt/cms.css +3 -3
- package/dist/module.d.mts +5 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +77 -46
- package/dist/runtime/components/DatePicker.vue +13 -1
- package/dist/runtime/components/FormPanel.vue +2 -0
- package/dist/runtime/components/{InputSeo.d.vue.ts → FormSeo.d.vue.ts} +10 -8
- package/dist/runtime/components/{InputSeo.vue → FormSeo.vue} +18 -8
- package/dist/runtime/components/{InputSeo.vue.d.ts → FormSeo.vue.d.ts} +10 -8
- package/dist/runtime/components/{InputSlug.vue.d.ts → FormSlug.d.vue.ts} +12 -8
- package/dist/runtime/components/{InputSlug.vue → FormSlug.vue} +17 -8
- package/dist/runtime/components/{InputSlug.d.vue.ts → FormSlug.vue.d.ts} +12 -8
- package/dist/runtime/components/FormUploraImage.d.vue.ts +43 -0
- package/dist/runtime/components/FormUploraImage.vue +172 -0
- package/dist/runtime/components/FormUploraImage.vue.d.ts +43 -0
- package/dist/runtime/components/TablePreviewLink.d.vue.ts +13 -0
- package/dist/runtime/components/TablePreviewLink.vue +38 -0
- package/dist/runtime/components/TablePreviewLink.vue.d.ts +13 -0
- package/dist/runtime/composables/useTableColumns.d.ts +3 -2
- package/dist/runtime/composables/useTableColumns.js +11 -12
- package/dist/runtime/editor/uplora-image/EditorUploraImageNode.vue +2 -2
- package/dist/runtime/server/api/uplora/[id].delete.js +1 -1
- package/dist/runtime/server/api/uplora/index.post.js +2 -4
- package/dist/runtime/server/errors/HttpError.d.ts +8 -0
- package/dist/runtime/server/errors/HttpError.js +7 -0
- package/dist/runtime/server/errors/index.d.ts +1 -1
- package/dist/runtime/server/errors/index.js +1 -1
- package/dist/runtime/server/utils/http.d.ts +43 -0
- package/dist/runtime/server/utils/http.js +86 -0
- package/dist/runtime/server/utils/validation.js +6 -7
- package/dist/runtime/types/index.d.ts +3 -3
- package/dist/runtime/types/index.js +3 -3
- package/dist/runtime/utils/enums.d.ts +4 -0
- package/dist/runtime/utils/enums.js +6 -0
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +1 -0
- package/package.json +7 -6
- package/.nuxt/cms/http-codes.ts +0 -8
- package/dist/runtime/components/InputUploraImage.d.vue.ts +0 -40
- package/dist/runtime/components/InputUploraImage.vue +0 -181
- package/dist/runtime/components/InputUploraImage.vue.d.ts +0 -40
- package/dist/runtime/server/errors/InternalHttpError.d.ts +0 -8
- package/dist/runtime/server/errors/InternalHttpError.js +0 -7
- package/dist/runtime/server/utils/errors.d.ts +0 -8
- package/dist/runtime/server/utils/errors.js +0 -57
- package/dist/runtime/server/utils/httpHandler.d.ts +0 -10
- package/dist/runtime/server/utils/httpHandler.js +0 -15
- /package/.nuxt/cms/{input-seo.ts → form-seo.ts} +0 -0
- /package/.nuxt/cms/{input-slug.ts → form-slug.ts} +0 -0
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import type { AppConfig } from '@nuxt/schema';
|
|
2
2
|
import type { InputProps } from '@nuxt/ui';
|
|
3
3
|
import type { ComponentConfig } from '../types';
|
|
4
|
-
import theme from '#build/cms/
|
|
5
|
-
type
|
|
6
|
-
export interface
|
|
4
|
+
import theme from '#build/cms/form-slug';
|
|
5
|
+
type FormSlug = ComponentConfig<typeof theme, AppConfig, 'formSlug'>;
|
|
6
|
+
export interface FormSlugProps {
|
|
7
|
+
name?: string;
|
|
7
8
|
regenerate?: boolean;
|
|
8
9
|
label?: string;
|
|
9
10
|
titleKey?: string;
|
|
10
11
|
slugKey?: string;
|
|
11
12
|
inputProps?: Omit<InputProps, 'modelModifiers'>;
|
|
12
|
-
as?: any;
|
|
13
13
|
class?: any;
|
|
14
|
-
ui?:
|
|
14
|
+
ui?: FormSlug['slots'];
|
|
15
15
|
}
|
|
16
16
|
declare const _default: typeof __VLS_export;
|
|
17
17
|
export default _default;
|
|
18
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
18
|
+
declare const __VLS_export: import("vue").DefineComponent<FormSlugProps & {
|
|
19
19
|
title?: string;
|
|
20
20
|
slug?: string;
|
|
21
21
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
22
22
|
"update:title": (value: string | undefined) => any;
|
|
23
23
|
"update:slug": (value: string | undefined) => any;
|
|
24
|
-
}, string, import("vue").PublicProps, Readonly<
|
|
24
|
+
}, string, import("vue").PublicProps, Readonly<FormSlugProps & {
|
|
25
25
|
title?: string;
|
|
26
26
|
slug?: string;
|
|
27
27
|
}> & Readonly<{
|
|
28
28
|
"onUpdate:title"?: ((value: string | undefined) => any) | undefined;
|
|
29
29
|
"onUpdate:slug"?: ((value: string | undefined) => any) | undefined;
|
|
30
|
-
}>, {
|
|
30
|
+
}>, {
|
|
31
|
+
name: string;
|
|
32
|
+
titleKey: string;
|
|
33
|
+
slugKey: string;
|
|
34
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { ComponentConfig } from '../types';
|
|
3
|
+
import theme from '#build/cms/form-uplora-image';
|
|
4
|
+
type FormUploraImage = ComponentConfig<typeof theme, AppConfig, 'formUploraImage'>;
|
|
5
|
+
export interface FormUploraImageModelValue {
|
|
6
|
+
image?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
lqip?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FormUploraImageProps {
|
|
12
|
+
showExtensions?: boolean;
|
|
13
|
+
id?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
as?: any;
|
|
18
|
+
class?: any;
|
|
19
|
+
nested?: boolean;
|
|
20
|
+
ui?: FormUploraImage['slots'];
|
|
21
|
+
}
|
|
22
|
+
export interface FormUploraImageEmits {
|
|
23
|
+
upload: [FormUploraImageModelValue];
|
|
24
|
+
delete: [];
|
|
25
|
+
}
|
|
26
|
+
declare const _default: typeof __VLS_export;
|
|
27
|
+
export default _default;
|
|
28
|
+
declare const __VLS_export: import("vue").DefineComponent<FormUploraImageProps & {
|
|
29
|
+
modelValue?: FormUploraImageModelValue;
|
|
30
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
31
|
+
delete: () => any;
|
|
32
|
+
"update:modelValue": (value: FormUploraImageModelValue) => any;
|
|
33
|
+
upload: (args_0: FormUploraImageModelValue) => any;
|
|
34
|
+
}, string, import("vue").PublicProps, Readonly<FormUploraImageProps & {
|
|
35
|
+
modelValue?: FormUploraImageModelValue;
|
|
36
|
+
}> & Readonly<{
|
|
37
|
+
onDelete?: (() => any) | undefined;
|
|
38
|
+
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue) => any) | undefined;
|
|
39
|
+
onUpload?: ((args_0: FormUploraImageModelValue) => any) | undefined;
|
|
40
|
+
}>, {
|
|
41
|
+
showExtensions: boolean;
|
|
42
|
+
nested: boolean;
|
|
43
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<UForm
|
|
3
|
+
:name="name"
|
|
4
|
+
:schema="schema"
|
|
5
|
+
:class="ui.root({ class: [props.ui?.root, props.class] })"
|
|
6
|
+
:validate-on="['change']"
|
|
7
|
+
:nested="nested"
|
|
8
|
+
>
|
|
9
|
+
<UFormField name="image" :label="label">
|
|
10
|
+
<div :class="ui.base({ class: [props.ui?.base] })">
|
|
11
|
+
<template v-if="modelValue.image">
|
|
12
|
+
<UPopover :content="{ align: 'center', side: 'top', sideOffset: 8 }" :ui="{ content: 'p-1' }">
|
|
13
|
+
<CUploraImage
|
|
14
|
+
:image="modelValue.image"
|
|
15
|
+
:alt="modelValue.alt"
|
|
16
|
+
:color="modelValue.color"
|
|
17
|
+
:lqip="modelValue.lqip"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<template #content>
|
|
21
|
+
<div class="flex items-center gap-0.5">
|
|
22
|
+
<UButton
|
|
23
|
+
color="neutral"
|
|
24
|
+
size="sm"
|
|
25
|
+
variant="ghost"
|
|
26
|
+
:icon="appConfig.ui.icons.refresh"
|
|
27
|
+
@click="resetState()"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<ButtonDeleteConfirm
|
|
31
|
+
color="error"
|
|
32
|
+
size="sm"
|
|
33
|
+
variant="ghost"
|
|
34
|
+
label=""
|
|
35
|
+
:icon="appConfig.ui.icons.trash"
|
|
36
|
+
@confirm="deleteExecute(modelValue.image)"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
</UPopover>
|
|
41
|
+
</template>
|
|
42
|
+
<div v-else :id="id" :class="ui.uploader({ class: [props.ui?.uploader] })">
|
|
43
|
+
<template v-if="uploadStatus === 'pending'">
|
|
44
|
+
<UIcon :name="appConfig.ui.icons.loading" :class="ui.uploaderPendingIcon({ class: [props.ui?.uploaderPendingIcon] })" />
|
|
45
|
+
</template>
|
|
46
|
+
<template v-else-if="uploadStatus === 'idle'">
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
:class="ui.uploaderIdleButton({ class: [props.ui?.uploaderIdleButton] })"
|
|
50
|
+
:disabled="disabled"
|
|
51
|
+
v-bind="ariaAttrs"
|
|
52
|
+
@click="open"
|
|
53
|
+
>
|
|
54
|
+
<UAvatar :icon="appConfig.ui.icons.image" size="lg" />
|
|
55
|
+
|
|
56
|
+
<p :class="ui.uploaderIdleText({ class: [props.ui?.uploaderIdleText] })">
|
|
57
|
+
Загрузить изображение
|
|
58
|
+
</p>
|
|
59
|
+
<p :class="ui.uploaderIdleExtensions({ class: [props.ui?.uploaderIdleExtensions] })">
|
|
60
|
+
{{ extensions }}
|
|
61
|
+
</p>
|
|
62
|
+
</button>
|
|
63
|
+
</template>
|
|
64
|
+
<template v-else-if="uploadStatus === 'error'">
|
|
65
|
+
<p :class="ui.uploaderErrorText({ class: [props.ui?.uploaderErrorText] })">
|
|
66
|
+
Произошла ошибка при загрузке изображения
|
|
67
|
+
</p>
|
|
68
|
+
<div :class="ui.uploaderErrorActions({ class: [props.ui?.uploaderErrorActions] })">
|
|
69
|
+
<UButton
|
|
70
|
+
label="Отменить"
|
|
71
|
+
variant="ghost"
|
|
72
|
+
color="neutral"
|
|
73
|
+
@click="resetUpload"
|
|
74
|
+
/>
|
|
75
|
+
<UButton
|
|
76
|
+
label="Повторить"
|
|
77
|
+
variant="soft"
|
|
78
|
+
color="neutral"
|
|
79
|
+
@click="uploadExecute"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</UFormField>
|
|
86
|
+
<UFormField name="alt">
|
|
87
|
+
<UInput
|
|
88
|
+
v-model="modelValue.alt"
|
|
89
|
+
placeholder="Введите описание..."
|
|
90
|
+
variant="none"
|
|
91
|
+
size="xs"
|
|
92
|
+
:ui="{ base: 'text-center' }"
|
|
93
|
+
/>
|
|
94
|
+
</UFormField>
|
|
95
|
+
</UForm>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<script>
|
|
99
|
+
import theme from "#build/cms/form-uplora-image";
|
|
100
|
+
import { useAppConfig, useFormField, useUploraDelete, useUploraUpload } from "#imports";
|
|
101
|
+
import { imagesExtensions } from "@uplora/formats";
|
|
102
|
+
import { computed, useId } from "vue";
|
|
103
|
+
import { z } from "zod";
|
|
104
|
+
import { tv } from "../tv";
|
|
105
|
+
import ButtonDeleteConfirm from "./ButtonDeleteConfirm.vue";
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<script setup>
|
|
109
|
+
const props = defineProps({
|
|
110
|
+
showExtensions: { type: Boolean, required: false, default: true },
|
|
111
|
+
id: { type: String, required: false },
|
|
112
|
+
name: { type: String, required: false },
|
|
113
|
+
label: { type: String, required: false },
|
|
114
|
+
disabled: { type: Boolean, required: false },
|
|
115
|
+
as: { type: null, required: false },
|
|
116
|
+
class: { type: null, required: false },
|
|
117
|
+
nested: { type: Boolean, required: false, default: true },
|
|
118
|
+
ui: { type: null, required: false }
|
|
119
|
+
});
|
|
120
|
+
const emit = defineEmits(["upload", "delete"]);
|
|
121
|
+
const modelValue = defineModel({ type: Object, ...{
|
|
122
|
+
default: () => ({
|
|
123
|
+
image: void 0,
|
|
124
|
+
alt: void 0,
|
|
125
|
+
lqip: void 0,
|
|
126
|
+
color: void 0
|
|
127
|
+
})
|
|
128
|
+
} });
|
|
129
|
+
const appConfig = useAppConfig();
|
|
130
|
+
const schema = z.object({
|
|
131
|
+
image: z.string().min(1),
|
|
132
|
+
alt: z.string().min(1),
|
|
133
|
+
lqip: z.string().min(1).optional(),
|
|
134
|
+
color: z.string().min(1).optional()
|
|
135
|
+
});
|
|
136
|
+
const extensions = computed(() => imagesExtensions.filter((extension) => extension !== "jpeg").join(", "));
|
|
137
|
+
const { id: _id, disabled, emitFormChange, emitFormInput, ariaAttrs } = useFormField(props);
|
|
138
|
+
const id = _id.value ?? useId();
|
|
139
|
+
const { open, execute: uploadExecute, status: uploadStatus, reset: resetUpload, onUploaded } = useUploraUpload({
|
|
140
|
+
accept: "image/*"
|
|
141
|
+
});
|
|
142
|
+
const { execute: deleteExecute, onDeleted } = useUploraDelete();
|
|
143
|
+
function resetState() {
|
|
144
|
+
modelValue.value = {
|
|
145
|
+
image: void 0,
|
|
146
|
+
alt: void 0,
|
|
147
|
+
lqip: void 0,
|
|
148
|
+
color: void 0
|
|
149
|
+
};
|
|
150
|
+
emitFormChange();
|
|
151
|
+
emitFormInput();
|
|
152
|
+
}
|
|
153
|
+
onUploaded((file) => {
|
|
154
|
+
modelValue.value = {
|
|
155
|
+
...modelValue.value,
|
|
156
|
+
image: file.id,
|
|
157
|
+
lqip: file.lqip,
|
|
158
|
+
color: file.color
|
|
159
|
+
};
|
|
160
|
+
emit("upload", modelValue.value);
|
|
161
|
+
emitFormChange();
|
|
162
|
+
emitFormInput();
|
|
163
|
+
});
|
|
164
|
+
onDeleted(() => {
|
|
165
|
+
resetUpload();
|
|
166
|
+
resetState();
|
|
167
|
+
emit("delete");
|
|
168
|
+
});
|
|
169
|
+
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.formUploraImage || {} })({
|
|
170
|
+
disabled: disabled.value
|
|
171
|
+
}));
|
|
172
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { ComponentConfig } from '../types';
|
|
3
|
+
import theme from '#build/cms/form-uplora-image';
|
|
4
|
+
type FormUploraImage = ComponentConfig<typeof theme, AppConfig, 'formUploraImage'>;
|
|
5
|
+
export interface FormUploraImageModelValue {
|
|
6
|
+
image?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
lqip?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FormUploraImageProps {
|
|
12
|
+
showExtensions?: boolean;
|
|
13
|
+
id?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
as?: any;
|
|
18
|
+
class?: any;
|
|
19
|
+
nested?: boolean;
|
|
20
|
+
ui?: FormUploraImage['slots'];
|
|
21
|
+
}
|
|
22
|
+
export interface FormUploraImageEmits {
|
|
23
|
+
upload: [FormUploraImageModelValue];
|
|
24
|
+
delete: [];
|
|
25
|
+
}
|
|
26
|
+
declare const _default: typeof __VLS_export;
|
|
27
|
+
export default _default;
|
|
28
|
+
declare const __VLS_export: import("vue").DefineComponent<FormUploraImageProps & {
|
|
29
|
+
modelValue?: FormUploraImageModelValue;
|
|
30
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
31
|
+
delete: () => any;
|
|
32
|
+
"update:modelValue": (value: FormUploraImageModelValue) => any;
|
|
33
|
+
upload: (args_0: FormUploraImageModelValue) => any;
|
|
34
|
+
}, string, import("vue").PublicProps, Readonly<FormUploraImageProps & {
|
|
35
|
+
modelValue?: FormUploraImageModelValue;
|
|
36
|
+
}> & Readonly<{
|
|
37
|
+
onDelete?: (() => any) | undefined;
|
|
38
|
+
"onUpdate:modelValue"?: ((value: FormUploraImageModelValue) => any) | undefined;
|
|
39
|
+
onUpload?: ((args_0: FormUploraImageModelValue) => any) | undefined;
|
|
40
|
+
}>, {
|
|
41
|
+
showExtensions: boolean;
|
|
42
|
+
nested: boolean;
|
|
43
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ButtonProps } from '@nuxt/ui';
|
|
2
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
3
|
+
import type { UploraImageProps } from './UploraImage.vue';
|
|
4
|
+
export interface TablePreviewLinkProps {
|
|
5
|
+
to?: RouteLocationRaw;
|
|
6
|
+
target?: ButtonProps['target'];
|
|
7
|
+
label: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
image?: Pick<UploraImageProps, 'image' | 'alt' | 'color' | 'lqip'>;
|
|
10
|
+
}
|
|
11
|
+
declare const __VLS_export: import("vue").DefineComponent<TablePreviewLinkProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TablePreviewLinkProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative flex items-center gap-2">
|
|
3
|
+
<div v-if="image" class="overflow-hidden rounded-lg">
|
|
4
|
+
<UploraImage
|
|
5
|
+
v-bind="image"
|
|
6
|
+
:sizes="[{ width: 32, height: 32, descriptor: '1x' }]"
|
|
7
|
+
:ui="{ picture: 'aspect-square' }"
|
|
8
|
+
/>
|
|
9
|
+
</div>
|
|
10
|
+
<div>
|
|
11
|
+
<ULink
|
|
12
|
+
v-if="to"
|
|
13
|
+
:to="to"
|
|
14
|
+
:target="target"
|
|
15
|
+
class="text-primary font-medium"
|
|
16
|
+
>
|
|
17
|
+
{{ label }}
|
|
18
|
+
</ULink>
|
|
19
|
+
<p v-else class="font-medium text-highlighted text-sm">
|
|
20
|
+
{{ label }}
|
|
21
|
+
</p>
|
|
22
|
+
<p v-if="description" class="text-muted text-xs">
|
|
23
|
+
{{ description }}
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup>
|
|
30
|
+
import UploraImage from "./UploraImage.vue";
|
|
31
|
+
defineProps({
|
|
32
|
+
to: { type: null, required: false },
|
|
33
|
+
target: { type: [String, Object, null], required: false },
|
|
34
|
+
label: { type: String, required: true },
|
|
35
|
+
description: { type: String, required: false },
|
|
36
|
+
image: { type: Object, required: false }
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ButtonProps } from '@nuxt/ui';
|
|
2
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
3
|
+
import type { UploraImageProps } from './UploraImage.vue';
|
|
4
|
+
export interface TablePreviewLinkProps {
|
|
5
|
+
to?: RouteLocationRaw;
|
|
6
|
+
target?: ButtonProps['target'];
|
|
7
|
+
label: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
image?: Pick<UploraImageProps, 'image' | 'alt' | 'color' | 'lqip'>;
|
|
10
|
+
}
|
|
11
|
+
declare const __VLS_export: import("vue").DefineComponent<TablePreviewLinkProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TablePreviewLinkProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
declare const _default: typeof __VLS_export;
|
|
13
|
+
export default _default;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ButtonProps, FormSchema, TableColumn, TableData, TableRow } from '@nuxt/ui';
|
|
2
2
|
import type { RowData } from '@tanstack/table-core';
|
|
3
3
|
import type { RouteLocationRaw } from 'vue-router';
|
|
4
|
+
import type { TablePreviewLinkProps } from '../components/TablePreviewLink.vue';
|
|
4
5
|
import type { FiltersField } from '../types';
|
|
5
6
|
declare module '@tanstack/table-core' {
|
|
6
7
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
@@ -18,8 +19,8 @@ export type TableColumnCustom<T extends TableData, D = unknown> = TableColumn<T,
|
|
|
18
19
|
to?: ((row: TableRow<T>) => RouteLocationRaw | string | undefined) | RouteLocationRaw | string | undefined;
|
|
19
20
|
target?: ButtonProps['target'];
|
|
20
21
|
emptyValue?: string;
|
|
21
|
-
image?:
|
|
22
|
-
description?: string;
|
|
22
|
+
image?: ((row: TableRow<T>) => TablePreviewLinkProps['image'] | undefined) | TablePreviewLinkProps['image'] | undefined;
|
|
23
|
+
description?: ((row: TableRow<T>) => string | undefined) | string | undefined;
|
|
23
24
|
};
|
|
24
25
|
export declare function useTableColumns<I extends Record<string, any>, T extends readonly TableColumnCustom<I>[] = readonly TableColumnCustom<I>[]>(columns: T): {
|
|
25
26
|
columns: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { UUser } from "#components";
|
|
2
1
|
import { h } from "vue";
|
|
2
|
+
import TablePreviewLink from "../components/TablePreviewLink.vue";
|
|
3
3
|
import { formatDate, formatDateTime, formatNumber } from "../utils/index.js";
|
|
4
4
|
export function useTableFiltersFields(_schema, fields) {
|
|
5
5
|
return { filtersFields: fields };
|
|
@@ -28,17 +28,16 @@ function transformColumn(column) {
|
|
|
28
28
|
return void 0;
|
|
29
29
|
}
|
|
30
30
|
if (column.to) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
description: column.description
|
|
31
|
+
const to = typeof column.to === "function" ? column.to(row) : column.to;
|
|
32
|
+
const target = column.target;
|
|
33
|
+
const description = typeof column.description === "function" ? column.description(row) : column.description;
|
|
34
|
+
const image = typeof column.image === "function" ? column.image(row) : column.image;
|
|
35
|
+
return h(TablePreviewLink, {
|
|
36
|
+
to,
|
|
37
|
+
target,
|
|
38
|
+
description,
|
|
39
|
+
label: value.toString(),
|
|
40
|
+
image
|
|
42
41
|
});
|
|
43
42
|
}
|
|
44
43
|
return value;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<NodeViewWrapper>
|
|
3
|
-
<
|
|
3
|
+
<FormUploraImage v-model="attrs" :nested="false" />
|
|
4
4
|
</NodeViewWrapper>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script setup>
|
|
8
8
|
import { NodeViewWrapper } from "@tiptap/vue-3";
|
|
9
9
|
import { computed } from "vue";
|
|
10
|
-
import
|
|
10
|
+
import FormUploraImage from "../../components/FormUploraImage.vue";
|
|
11
11
|
const props = defineProps({
|
|
12
12
|
decorations: { type: Array, required: true },
|
|
13
13
|
selected: { type: Boolean, required: true },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#imports";
|
|
2
2
|
import { getValidatedRouterParams } from "h3";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { defineHttpHandler } from "../../utils/
|
|
4
|
+
import { defineHttpHandler } from "../../utils/http.js";
|
|
5
5
|
export const filesDeleteRouteParamsSchema = z.object({
|
|
6
6
|
id: z.string().min(24).max(24)
|
|
7
7
|
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import { HTTP_CODE_BAD_REQUEST } from "#cms/http-codes";
|
|
3
2
|
import { useRuntimeConfig } from "#imports";
|
|
4
3
|
import { isImageMimeType } from "@uplora/formats";
|
|
5
4
|
import { z } from "zod";
|
|
6
|
-
import {
|
|
7
|
-
import { defineHttpHandler } from "../../utils/httpHandler.js";
|
|
5
|
+
import { createHttpError, defineHttpHandler } from "../../utils/http.js";
|
|
8
6
|
import { readValidatedMultipartFormData } from "../../utils/validation.js";
|
|
9
7
|
const filesCreatePayloadSchema = z.object({
|
|
10
8
|
file: z.object({
|
|
@@ -18,7 +16,7 @@ export default defineHttpHandler(async (event) => {
|
|
|
18
16
|
const { file } = await readValidatedMultipartFormData(event, filesCreatePayloadSchema.parse);
|
|
19
17
|
const { uplora } = useRuntimeConfig();
|
|
20
18
|
if (!file) {
|
|
21
|
-
throw
|
|
19
|
+
throw createHttpError("badRequest");
|
|
22
20
|
}
|
|
23
21
|
const formData = new FormData();
|
|
24
22
|
formData.append("file", new Blob([file.data], { type: file.type }), file.filename);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HttpStatusKey } from '#cms/http-statuses';
|
|
2
|
+
import type { H3Error } from 'h3';
|
|
3
|
+
export type HttpErrorOptions = Partial<Pick<H3Error, 'cause' | 'fatal' | 'data'>>;
|
|
4
|
+
export declare class HttpError extends Error {
|
|
5
|
+
readonly httpStatusKey: HttpStatusKey;
|
|
6
|
+
readonly options?: HttpErrorOptions | undefined;
|
|
7
|
+
constructor(httpStatusKey: HttpStatusKey, options?: HttpErrorOptions | undefined);
|
|
8
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './HttpError';
|
|
2
2
|
export * from './TimeoutError';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./HttpError.js";
|
|
2
2
|
export * from "./TimeoutError.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { HttpStatusKey } from '#cms/http-statuses';
|
|
2
|
+
import type { EventHandler, EventHandlerRequest, EventHandlerResponse, H3Event } from 'h3';
|
|
3
|
+
import type { CachedEventHandlerOptions } from 'nitropack/types';
|
|
4
|
+
import type { HttpErrorOptions } from '../errors';
|
|
5
|
+
import { H3Error } from 'h3';
|
|
6
|
+
import { HttpError } from '../errors';
|
|
7
|
+
type HttpStatusesCodesMap = Record<string, HttpStatusKey>;
|
|
8
|
+
interface DefineHttpHandlerOptions<Response extends EventHandlerResponse> {
|
|
9
|
+
httpCodesMap?: HttpStatusesCodesMap;
|
|
10
|
+
cache?: CachedEventHandlerOptions<Response>;
|
|
11
|
+
access?: 'admin' | 'employee' | 'user';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Разрешает и форматирует ошибку для HTTP ответов сервера.
|
|
15
|
+
* Логирует ошибку и возвращает отформатированную H3Error с кодом статуса и сообщением.
|
|
16
|
+
*
|
|
17
|
+
* @param event - Объект события.
|
|
18
|
+
* @param error - Ошибка для разрешения.
|
|
19
|
+
* @param httpCodesMap - Опциональное пользовательское сопоставление типов ошибок с ключами статусов.
|
|
20
|
+
* @returns Отформатированная H3Error для ответа.
|
|
21
|
+
*/
|
|
22
|
+
export declare function errorServerResolver(event: H3Event, error: unknown, httpCodesMap?: HttpStatusesCodesMap): Promise<H3Error<unknown>>;
|
|
23
|
+
/**
|
|
24
|
+
* Создает новый экземпляр HttpError с заданным ключом статуса и опциями.
|
|
25
|
+
*
|
|
26
|
+
* @param errorStatusKey - Ключ, представляющий HTTP статус.
|
|
27
|
+
* @param options - Дополнительные опции ошибки (cause, fatal).
|
|
28
|
+
* @returns Созданный экземпляр HttpError.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createHttpError(errorStatusKey: HttpStatusKey, options?: HttpErrorOptions): HttpError;
|
|
31
|
+
/**
|
|
32
|
+
* Определяет HTTP обработчик, который оборачивает предоставленный обработчик и обрабатывает ошибки.
|
|
33
|
+
* Перехватывает и форматирует ошибки для HTTP ответов, включая ошибки валидации и базы данных.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam Request - Тип объекта запроса.
|
|
36
|
+
* @typeParam Response - Тип объекта ответа.
|
|
37
|
+
* @param handler - Основная функция обработчика.
|
|
38
|
+
* @param options - Опциональное пользовательское сопоставление типов ошибок с ключами статусов.
|
|
39
|
+
* @param options.httpCodesMap - Опциональное пользовательское сопоставление типов ошибок с ключами статусов.
|
|
40
|
+
* @returns Обернутый обработчик событий с обработкой ошибок.
|
|
41
|
+
*/
|
|
42
|
+
export declare function defineHttpHandler<Request extends EventHandlerRequest, Response extends EventHandlerResponse>(handler: EventHandler<Request, Response | Promise<Response>>, options?: DefineHttpHandlerOptions<Response>): EventHandler<Request, Promise<Response>>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { httpStatuses } from "#cms/http-statuses";
|
|
2
|
+
import defu from "defu";
|
|
3
|
+
import { DrizzleQueryError } from "drizzle-orm/errors";
|
|
4
|
+
import { createError, eventHandler, H3Error } from "h3";
|
|
5
|
+
import { cachedEventHandler } from "nitropack/runtime";
|
|
6
|
+
import { camelCase } from "scule";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { logger } from "../../utils/logger.js";
|
|
9
|
+
import { HttpError } from "../errors/index.js";
|
|
10
|
+
const defaultHttpCodes = {
|
|
11
|
+
default: "internalServerError",
|
|
12
|
+
zod: "badRequest",
|
|
13
|
+
db: "internalServerError"
|
|
14
|
+
};
|
|
15
|
+
const excludedLoggedStatuses = ["unauthorized"];
|
|
16
|
+
function extractHttpStatusKey(error, httpCodesMap = {}) {
|
|
17
|
+
const codesMap = defu(httpCodesMap, defaultHttpCodes);
|
|
18
|
+
if (error instanceof HttpError) {
|
|
19
|
+
return error.httpStatusKey;
|
|
20
|
+
} else if (error instanceof z.ZodError) {
|
|
21
|
+
return codesMap.zod;
|
|
22
|
+
} else if (error instanceof DrizzleQueryError) {
|
|
23
|
+
const maybeStatus = `db_${error.cause?.code}`;
|
|
24
|
+
return codesMap[maybeStatus] ?? codesMap.db;
|
|
25
|
+
} else if (error instanceof H3Error && error.cause instanceof HttpError) {
|
|
26
|
+
return error.cause.httpStatusKey;
|
|
27
|
+
} else if (error instanceof Error) {
|
|
28
|
+
const maybeStatus = camelCase(error.message);
|
|
29
|
+
return codesMap[maybeStatus] ?? codesMap.default;
|
|
30
|
+
}
|
|
31
|
+
return codesMap.default;
|
|
32
|
+
}
|
|
33
|
+
function enrichHttpError(error) {
|
|
34
|
+
if (error instanceof z.ZodError) {
|
|
35
|
+
return { data: error.issues };
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof HttpError) {
|
|
38
|
+
return { data: error.options?.data };
|
|
39
|
+
}
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
export async function errorServerResolver(event, error, httpCodesMap = {}) {
|
|
43
|
+
const httpStatusKey = extractHttpStatusKey(error, httpCodesMap);
|
|
44
|
+
const httpStatusCode = httpStatuses[httpStatusKey].code;
|
|
45
|
+
const httpStatusMessage = httpStatuses[httpStatusKey].message;
|
|
46
|
+
if (!excludedLoggedStatuses.includes(httpStatusKey)) {
|
|
47
|
+
logger.error.raw({
|
|
48
|
+
key: httpStatusKey,
|
|
49
|
+
code: httpStatusCode,
|
|
50
|
+
path: event.path,
|
|
51
|
+
method: event.method,
|
|
52
|
+
...error instanceof HttpError ? { data: JSON.stringify(error.options?.data) } : { error: JSON.stringify(error) }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return createError(defu(
|
|
56
|
+
{
|
|
57
|
+
statusCode: httpStatusCode,
|
|
58
|
+
message: httpStatusMessage
|
|
59
|
+
},
|
|
60
|
+
enrichHttpError(error)
|
|
61
|
+
));
|
|
62
|
+
}
|
|
63
|
+
export function createHttpError(errorStatusKey, options) {
|
|
64
|
+
return new HttpError(errorStatusKey, options);
|
|
65
|
+
}
|
|
66
|
+
export function defineHttpHandler(handler, options) {
|
|
67
|
+
async function handlerWrapper(event) {
|
|
68
|
+
try {
|
|
69
|
+
return await handler(event);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
let error = e;
|
|
72
|
+
if (error.cause?.data instanceof Error) {
|
|
73
|
+
error = error.cause?.data;
|
|
74
|
+
}
|
|
75
|
+
throw await errorServerResolver(event, error, options?.httpCodesMap);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (options?.cache) {
|
|
79
|
+
return cachedEventHandler(handlerWrapper, {
|
|
80
|
+
group: "http",
|
|
81
|
+
swr: false,
|
|
82
|
+
...options.cache
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return eventHandler(handlerWrapper);
|
|
86
|
+
}
|