mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-09 20:12:10 +10:00
Combined fixes (#116)
* fix: missing CheckIcon import in LanguageSelector * fix: #114 and error handling * fix: #97 * fix: lint * feat: #104 * fix: #72
This commit is contained in:
@ -107,7 +107,10 @@ import {
|
|||||||
ListboxOptions,
|
ListboxOptions,
|
||||||
} from "@headlessui/vue";
|
} from "@headlessui/vue";
|
||||||
import { ChevronUpDownIcon } from "@heroicons/vue/16/solid";
|
import { ChevronUpDownIcon } from "@heroicons/vue/16/solid";
|
||||||
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/24/outline";
|
import {
|
||||||
|
ArrowTopRightOnSquareIcon,
|
||||||
|
CheckIcon,
|
||||||
|
} from "@heroicons/vue/24/outline";
|
||||||
import type { Locale } from "vue-i18n";
|
import type { Locale } from "vue-i18n";
|
||||||
|
|
||||||
const { locales, locale: currLocale, setLocale } = useI18n();
|
const { locales, locale: currLocale, setLocale } = useI18n();
|
||||||
|
|||||||
@ -193,6 +193,7 @@
|
|||||||
<LoadingButton
|
<LoadingButton
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
class="bg-blue-600 text-white hover:bg-blue-500"
|
class="bg-blue-600 text-white hover:bg-blue-500"
|
||||||
|
:disabled="!isValidArticle"
|
||||||
@click="() => createArticle()"
|
@click="() => createArticle()"
|
||||||
>
|
>
|
||||||
{{ $t("news.article.submit") }}
|
{{ $t("news.article.submit") }}
|
||||||
@ -235,6 +236,13 @@ const newArticle = ref({
|
|||||||
tags: [] as string[],
|
tags: [] as string[],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isValidArticle = computed(
|
||||||
|
() =>
|
||||||
|
newArticle.value.title &&
|
||||||
|
newArticle.value.description &&
|
||||||
|
newArticle.value.content,
|
||||||
|
);
|
||||||
|
|
||||||
const markdownPreview = computed(() => {
|
const markdownPreview = computed(() => {
|
||||||
// TODO: maybe?? add https://github.com/cure53/DOMPurify
|
// TODO: maybe?? add https://github.com/cure53/DOMPurify
|
||||||
// micromark says its safe, but this is straight html we are injecting
|
// micromark says its safe, but this is straight html we are injecting
|
||||||
|
|||||||
@ -21,13 +21,18 @@ async function signIn() {
|
|||||||
redirect: `/auth/signin?redirect=${encodeURIComponent(route.fullPath)}`,
|
redirect: `/auth/signin?redirect=${encodeURIComponent(route.fullPath)}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
switch (statusCode) {
|
||||||
|
case 401:
|
||||||
|
case 403:
|
||||||
|
await signIn();
|
||||||
|
}
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: t("errors.pageTitle", [statusCode ?? message]),
|
title: t("errors.pageTitle", [statusCode ?? message]),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
console.log(props.error);
|
console.warn(props.error);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -254,5 +254,6 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
rateLimiter: false,
|
rateLimiter: false,
|
||||||
xssValidator: false,
|
xssValidator: false,
|
||||||
|
requestSizeLimiter: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
:disabled="notifications.length === 0"
|
:disabled="notifications.length === 0"
|
||||||
class="inline-flex items-center justify-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 shadow-sm transition-all duration-200 hover:bg-zinc-700 hover:scale-[1.02] hover:shadow-lg active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-zinc-800 disabled:hover:scale-100 disabled:hover:shadow-none"
|
class="inline-flex items-center justify-center gap-x-2 rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 shadow-sm transition-all duration-200 hover:bg-zinc-700 hover:scale-[1.02] hover:shadow-lg active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-zinc-800 disabled:hover:scale-100 disabled:hover:shadow-none"
|
||||||
@click="markAllAsRead"
|
@click="markAllAsRead"
|
||||||
>
|
>
|
||||||
<CheckIcon class="size-4" />
|
<CheckIcon class="size-4" />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
|
nuxtApp.hook("vue:error", (error, instance, info) => {
|
||||||
console.error(error, instance, info);
|
console.error(error, instance, info);
|
||||||
};
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
|
import { ArkErrors, type } from "arktype";
|
||||||
import { defineEventHandler, createError } from "h3";
|
import { defineEventHandler, createError } from "h3";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import newsManager from "~/server/internal/news";
|
import newsManager from "~/server/internal/news";
|
||||||
import { handleFileUpload } from "~/server/internal/utils/handlefileupload";
|
import { handleFileUpload } from "~/server/internal/utils/handlefileupload";
|
||||||
|
|
||||||
|
const CreateNews = type({
|
||||||
|
title: "string",
|
||||||
|
description: "string",
|
||||||
|
content: "string",
|
||||||
|
tags: "string = '[]'",
|
||||||
|
});
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const allowed = await aclManager.allowSystemACL(h3, ["news:create"]);
|
const allowed = await aclManager.allowSystemACL(h3, ["news:create"]);
|
||||||
if (!allowed) throw createError({ statusCode: 403 });
|
if (!allowed) throw createError({ statusCode: 403 });
|
||||||
@ -23,24 +31,25 @@ export default defineEventHandler(async (h3) => {
|
|||||||
|
|
||||||
const [imageIds, options, pull, _dump] = uploadResult;
|
const [imageIds, options, pull, _dump] = uploadResult;
|
||||||
|
|
||||||
const title = options.title;
|
const body = await CreateNews(options);
|
||||||
const description = options.description;
|
if (body instanceof ArkErrors)
|
||||||
const content = options.content;
|
throw createError({ statusCode: 400, statusMessage: body.summary });
|
||||||
const tags = options.tags ? (JSON.parse(options.tags) as string[]) : [];
|
|
||||||
const imageId = imageIds.at(0);
|
|
||||||
|
|
||||||
if (!title || !description || !content)
|
const parsedTags = JSON.parse(body.tags);
|
||||||
|
if (typeof parsedTags !== "object" || !Array.isArray(parsedTags))
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
statusMessage: "Missing or invalid title, description or content.",
|
statusMessage: "Tags must be an array",
|
||||||
});
|
});
|
||||||
|
|
||||||
const article = await newsManager.create({
|
const imageId = imageIds.at(0);
|
||||||
title: title,
|
|
||||||
description: description,
|
|
||||||
content: content,
|
|
||||||
|
|
||||||
tags: tags,
|
const article = await newsManager.create({
|
||||||
|
title: body.title,
|
||||||
|
description: body.description,
|
||||||
|
content: body.content,
|
||||||
|
|
||||||
|
tags: parsedTags,
|
||||||
|
|
||||||
...(imageId && { image: imageId }),
|
...(imageId && { image: imageId }),
|
||||||
authorId: "system",
|
authorId: "system",
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export class ObjectTransactionalHandler {
|
|||||||
if (!transaction) return;
|
if (!transaction) return;
|
||||||
|
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
const increment = (1 / transaction.size) * 100;
|
const increment = 100 / transaction.size;
|
||||||
|
|
||||||
for (const [id, data] of transaction) {
|
for (const [id, data] of transaction) {
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
|
|||||||
@ -374,8 +374,15 @@ export function wrapTaskContext(
|
|||||||
): TaskRunContext {
|
): TaskRunContext {
|
||||||
return {
|
return {
|
||||||
progress(progress) {
|
progress(progress) {
|
||||||
const scalar = 100 / (options.max - options.min);
|
if (progress > 100 || progress < 0) {
|
||||||
const adjustedProgress = progress * scalar + options.min;
|
console.warn("[wrapTaskContext] progress must be between 0 and 100");
|
||||||
|
}
|
||||||
|
|
||||||
|
// I was too tired to figure this out
|
||||||
|
// https://stackoverflow.com/a/929107
|
||||||
|
const oldRange = 100;
|
||||||
|
const newRange = options.max - options.min;
|
||||||
|
const adjustedProgress = (progress * newRange) / oldRange + options.min;
|
||||||
return context.progress(adjustedProgress);
|
return context.progress(adjustedProgress);
|
||||||
},
|
},
|
||||||
log(message) {
|
log(message) {
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import { H3Error } from "h3";
|
|
||||||
import sessionHandler from "../internal/session";
|
|
||||||
|
|
||||||
export default defineNitroPlugin((nitro) => {
|
|
||||||
nitro.hooks.hook("error", async (error, { event }) => {
|
|
||||||
if (!event) return;
|
|
||||||
|
|
||||||
// Don't handle for API routes
|
|
||||||
if (event.path.startsWith("/api")) return;
|
|
||||||
if (event.path.startsWith("/auth")) return;
|
|
||||||
|
|
||||||
// Make sure it's a web error
|
|
||||||
if (!(error instanceof H3Error)) return;
|
|
||||||
|
|
||||||
switch (error.statusCode) {
|
|
||||||
case 401:
|
|
||||||
case 403: {
|
|
||||||
const user = await sessionHandler.getSession(event);
|
|
||||||
if (user) break;
|
|
||||||
return sendRedirect(
|
|
||||||
event,
|
|
||||||
`/auth/signin?redirect=${encodeURIComponent(event.path)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user