style(*): cleanup unused imports
This commit is contained in:
parent
5baa00bbb4
commit
c8e84c909e
@ -3,6 +3,5 @@
|
|||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"arrowParens": "always",
|
"arrowParens": "always",
|
||||||
"vueIndentScriptAndStyle": true,
|
"vueIndentScriptAndStyle": true
|
||||||
"editorconfig": true
|
|
||||||
}
|
}
|
||||||
|
8
app.vue
8
app.vue
@ -1,14 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { theme } from "ant-design-vue";
|
|
||||||
|
|
||||||
const { getSession, signIn } = useAuth();
|
const { getSession, signIn } = useAuth();
|
||||||
|
|
||||||
await getSession({ force: true });
|
await getSession({ force: true });
|
||||||
|
|
||||||
const { data } = await useAuth();
|
const { data } = await useAuth();
|
||||||
|
|
||||||
const dop = data?.value as any;
|
let darkBool = ref(data.value?.user?.profile?.nightMode || false);
|
||||||
let darkBool = ref(dop?.user?.profile?.nightMode || false);
|
|
||||||
// provide("user", ref(dop?.user || null));
|
// provide("user", ref(dop?.user || null));
|
||||||
provide("dark", darkBool);
|
provide("dark", darkBool);
|
||||||
useHead({
|
useHead({
|
||||||
@ -17,8 +14,7 @@
|
|||||||
return darkBool.value ? "dark" : undefined;
|
return darkBool.value ? "dark" : undefined;
|
||||||
}).value,
|
}).value,
|
||||||
},
|
},
|
||||||
titleTemplate: (title) =>
|
titleTemplate: (title) => (title ? `Rockfic | ${title}` : "Rockfic | Band fiction that rocks"),
|
||||||
title ? `Rockfic | ${title}` : "Rockfic | Band fiction that rocks",
|
|
||||||
});
|
});
|
||||||
// provide("loaded", useNuxtApp().$loaded);
|
// provide("loaded", useNuxtApp().$loaded);
|
||||||
// let loaded = ref<boolean[]>([]);
|
// let loaded = ref<boolean[]>([]);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import TinymceEditor from "@tinymce/tinymce-vue";
|
import TinymceEditor from "@tinymce/tinymce-vue";
|
||||||
import { useDark } from "@vueuse/core";
|
|
||||||
import { NamePath } from "ant-design-vue/es/form/interface";
|
|
||||||
import { Field as VeeField } from "vee-validate";
|
import { Field as VeeField } from "vee-validate";
|
||||||
import tinymce from "tinymce";
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
name: string;
|
name: string;
|
||||||
init: any;
|
init: any;
|
||||||
@ -16,17 +14,8 @@
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<vee-field
|
<vee-field :name="props.name" v-slot="{ errorMessage, field, value }" :model-value="props.val">
|
||||||
:name="props.name"
|
<a-form-item :validate-status="!!errorMessage ? 'error' : ''" :name="props.name" :label="props.label as any" :help="errorMessage">
|
||||||
v-slot="{ errorMessage, field, value }"
|
|
||||||
:model-value="props.val"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
:validate-status="!!errorMessage ? 'error' : ''"
|
|
||||||
:name="props.name"
|
|
||||||
:label="props.label as any"
|
|
||||||
:help="errorMessage"
|
|
||||||
>
|
|
||||||
<tinymce-editor
|
<tinymce-editor
|
||||||
v-bind="field"
|
v-bind="field"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { log } from "@server/logger";
|
|
||||||
import { MenuProps } from "ant-design-vue";
|
|
||||||
|
|
||||||
const { data, status } = useAuth();
|
const { data, status } = useAuth();
|
||||||
const itemMap = ref({
|
const itemMap = ref({
|
||||||
home: "/",
|
home: "/",
|
||||||
@ -18,11 +15,7 @@
|
|||||||
admin: "/admin",
|
admin: "/admin",
|
||||||
logout: "/auth/logout",
|
logout: "/auth/logout",
|
||||||
});
|
});
|
||||||
let cur = ref<string>(
|
let cur = ref<string>(Object.keys(itemMap.value).find((a) => itemMap.value[a] === useRoute().path) || useRoute().path);
|
||||||
Object.keys(itemMap.value).find(
|
|
||||||
(a) => itemMap.value[a] === useRoute().path,
|
|
||||||
) || useRoute().path,
|
|
||||||
);
|
|
||||||
let selected: string[] = [cur.value];
|
let selected: string[] = [cur.value];
|
||||||
|
|
||||||
const clickFn = (minfo) => {
|
const clickFn = (minfo) => {
|
||||||
@ -61,18 +54,11 @@
|
|||||||
<a-menu-item key="reviews"> Manage Reviews </a-menu-item>
|
<a-menu-item key="reviews"> Manage Reviews </a-menu-item>
|
||||||
<a-menu-item key="messages"> Private Messages </a-menu-item>
|
<a-menu-item key="messages"> Private Messages </a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false">
|
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin </a-menu-item>
|
||||||
Admin
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
|
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
<div>
|
<div>
|
||||||
<a-button
|
<a-button v-if="data?.user" type="primary" tooltip="Post a New Story" @click="() => navigateTo('/new-story')">
|
||||||
v-if="data?.user"
|
|
||||||
type="primary"
|
|
||||||
tooltip="Post a New Story"
|
|
||||||
@click="() => navigateTo('/new-story')"
|
|
||||||
>
|
|
||||||
<!-- <template #icon>
|
<!-- <template #icon>
|
||||||
</template> -->
|
</template> -->
|
||||||
<icon istyle="regular" name="file-plus" />
|
<icon istyle="regular" name="file-plus" />
|
||||||
@ -80,16 +66,8 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="acbut" v-if="!data">
|
<div class="acbut" v-if="!data">
|
||||||
<a-button size="large" @click="() => navigateTo('/auth/login')">
|
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login </a-button>
|
||||||
Login
|
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register </a-button>
|
||||||
</a-button>
|
|
||||||
<a-button
|
|
||||||
size="large"
|
|
||||||
type="primary"
|
|
||||||
@click="() => navigateTo('/auth/register')"
|
|
||||||
>
|
|
||||||
Register
|
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
import type { MenuItemType, SubMenuType } from "ant-design-vue/es/menu/src/interface";
|
||||||
MenuItemType,
|
|
||||||
SubMenuType,
|
|
||||||
} from "ant-design-vue/es/menu/src/interface";
|
|
||||||
import { ItemType, theme } from "ant-design-vue";
|
import { ItemType, theme } from "ant-design-vue";
|
||||||
import Icon from "../icon.vue";
|
import Icon from "../icon.vue";
|
||||||
import { ISidebarItem } from "@models/sidebarEntry";
|
import { ISidebarItem } from "@models/sidebarEntry";
|
||||||
import { AButton, NuxtLink } from "#components";
|
import { NuxtLink } from "#components";
|
||||||
|
|
||||||
const loaded = inject<Ref<boolean>>("loaded");
|
const loaded = inject<Ref<boolean>>("loaded");
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IStory } from "@models/stories";
|
import type { IStory } from "@models/stories";
|
||||||
import { log } from "~/lib/server/logger";
|
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import icon from "../icon.vue";
|
import icon from "../icon.vue";
|
||||||
import { theme } from "ant-design-vue";
|
import { theme } from "ant-design-vue";
|
||||||
@ -14,18 +13,8 @@
|
|||||||
const idxo = (prop.last || false ? prop.story.chapters.length : 1) - 1;
|
const idxo = (prop.last || false ? prop.story.chapters.length : 1) - 1;
|
||||||
// console.log("idx0->", idxo)
|
// console.log("idx0->", idxo)
|
||||||
// log.debug("posti->", prop.story.chapters[ prop.story.chapters.length - 1 ]);
|
// log.debug("posti->", prop.story.chapters[ prop.story.chapters.length - 1 ]);
|
||||||
const shortDate = format(
|
const shortDate = format(Date.parse(prop.story.chapters[prop.story.chapters.length - 1]?.posted!.toString()), "yyyy/MM/dd");
|
||||||
Date.parse(
|
const longDate = format(Date.parse(prop.story.chapters[prop.story.chapters.length - 1]?.posted!.toString()), "iiii',' yyyy-MM-dd");
|
||||||
prop.story.chapters[prop.story.chapters.length - 1]?.posted!.toString(),
|
|
||||||
),
|
|
||||||
"yyyy/MM/dd",
|
|
||||||
);
|
|
||||||
const longDate = format(
|
|
||||||
Date.parse(
|
|
||||||
prop.story.chapters[prop.story.chapters.length - 1]?.posted!.toString(),
|
|
||||||
),
|
|
||||||
"iiii',' yyyy-MM-dd",
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a-card>
|
<a-card>
|
||||||
@ -36,26 +25,16 @@
|
|||||||
{{ story.title }}
|
{{ story.title }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<a-tooltip placement="topLeft">
|
<a-tooltip placement="topLeft">
|
||||||
<template #title>
|
<template #title> You'll need to log in to read this story. Register if you don't already have an account -- it's free! </template>
|
||||||
You'll need to log in to read this story. Register if you don't
|
|
||||||
already have an account -- it's free!
|
|
||||||
</template>
|
|
||||||
<icon v-if="!data?.user" istyle="solid" name="lock" />
|
<icon v-if="!data?.user" istyle="solid" name="lock" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div style="display: flex; font-size: 0.9em; align-items: baseline" class="headerthing">
|
||||||
style="display: flex; font-size: 0.9em; align-items: baseline"
|
|
||||||
class="headerthing"
|
|
||||||
>
|
|
||||||
<span> a </span>
|
<span> a </span>
|
||||||
<div
|
<div style="" v-for="(band, idx) in story.chapters[idxo].bands.filter((a) => !!a)">
|
||||||
style=""
|
|
||||||
v-for="(band, idx) in story.chapters[idxo].bands.filter((a) => !!a)"
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
<NuxtLink :to="`/band/${band._id}`"> {{ band.name }} </NuxtLink
|
<NuxtLink :to="`/band/${band._id}`"> {{ band.name }} </NuxtLink>{{ idx < story.chapters[idxo].bands.length - 1 ? ", " : "" }}
|
||||||
>{{ idx < story.chapters[idxo].bands.length - 1 ? ", " : "" }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span> fic by </span>
|
<span> fic by </span>
|
||||||
@ -100,9 +79,7 @@
|
|||||||
<div class="storyMeta">
|
<div class="storyMeta">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<span v-if="story.chapters.length > 1">
|
<span v-if="story.chapters.length > 1">
|
||||||
<NuxtLink :to="`/story/${story._id}/chapters`">
|
<NuxtLink :to="`/story/${story._id}/chapters`"> {{ story.chapters.length }} chapters </NuxtLink>
|
||||||
{{ story.chapters.length }} chapters
|
|
||||||
</NuxtLink>
|
|
||||||
</span>
|
</span>
|
||||||
<span v-else> {{ story.chapters.length }} chapter </span>
|
<span v-else> {{ story.chapters.length }} chapter </span>
|
||||||
<span>
|
<span>
|
||||||
@ -121,15 +98,10 @@
|
|||||||
{{ story.chapters[idxo].characters.join(", ") }}
|
{{ story.chapters[idxo].characters.join(", ") }}
|
||||||
</meta-item>
|
</meta-item>
|
||||||
<meta-item label="Relationship(s)">
|
<meta-item label="Relationship(s)">
|
||||||
<div
|
<div style="display: inline-block" v-for="(rel, idx) in story.chapters[idxo].relationships">
|
||||||
style="display: inline-block"
|
|
||||||
v-for="(rel, idx) in story.chapters[idxo].relationships"
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
{{ Array.isArray(rel) ? rel.join("/") : rel }}
|
{{ Array.isArray(rel) ? rel.join("/") : rel }}
|
||||||
{{
|
{{ idx < story.chapters[idxo].relationships.length - 1 ? "," : "" }}
|
||||||
idx < story.chapters[idxo].relationships.length - 1 ? "," : ""
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="story.chapters[idxo].relationships.length < 1">
|
<span v-if="story.chapters[idxo].relationships.length < 1">
|
||||||
@ -149,12 +121,7 @@
|
|||||||
<div class="stats">
|
<div class="stats">
|
||||||
<span>
|
<span>
|
||||||
<span class="staticon">
|
<span class="staticon">
|
||||||
<icon
|
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#ff2883" :size="12" name="heart" />
|
||||||
:istyle="!dark ? 'solid' : 'regular'"
|
|
||||||
icolor="#ff2883"
|
|
||||||
:size="12"
|
|
||||||
name="heart"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ story.favs }}
|
{{ story.favs }}
|
||||||
@ -162,12 +129,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="staticon">
|
<span class="staticon">
|
||||||
<icon
|
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#1787d7" :size="12" name="book-open" />
|
||||||
:istyle="!dark ? 'solid' : 'regular'"
|
|
||||||
icolor="#1787d7"
|
|
||||||
:size="12"
|
|
||||||
name="book-open"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ story.views }}
|
{{ story.views }}
|
||||||
@ -175,12 +137,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="staticon">
|
<span class="staticon">
|
||||||
<icon
|
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#51e07c" :size="12" name="thumbs-up" />
|
||||||
:istyle="!dark ? 'solid' : 'regular'"
|
|
||||||
icolor="#51e07c"
|
|
||||||
:size="12"
|
|
||||||
name="thumbs-up"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ story.recs }}
|
{{ story.recs }}
|
||||||
@ -188,12 +145,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="staticon">
|
<span class="staticon">
|
||||||
<icon
|
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#c2d420" :size="12" name="download" />
|
||||||
:istyle="!dark ? 'solid' : 'regular'"
|
|
||||||
icolor="#c2d420"
|
|
||||||
:size="12"
|
|
||||||
name="download"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ story.downloads }}
|
{{ story.downloads }}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Form as veeForm, Field as veeField, useForm } from "vee-validate";
|
import { Form as veeForm, Field as veeField } from "vee-validate";
|
||||||
import { IReview } from "@models/stories/review";
|
import { IReview } from "@models/stories/review";
|
||||||
import { comment } from "@client/editorConfig";
|
import { comment } from "@client/editorConfig";
|
||||||
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
|
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
|
||||||
@ -25,13 +25,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const replySubmit = async (values) => {
|
const replySubmit = async (values) => {
|
||||||
const { data } = await useApiFetch<any>(
|
const { data } = await useApiFetch<any>(`/review/${props.review._id}/reply`, {
|
||||||
`/review/${props.review._id}/reply`,
|
method: "post",
|
||||||
{
|
body: values,
|
||||||
method: "post",
|
});
|
||||||
body: values,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
review.value.replies.push(data.value.data);
|
review.value.replies.push(data.value.data);
|
||||||
replyFormVisible.value = false;
|
replyFormVisible.value = false;
|
||||||
};
|
};
|
||||||
@ -49,12 +46,7 @@
|
|||||||
<a-comment>
|
<a-comment>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<div v-if="!!data?.user" class="review-actions">
|
<div v-if="!!data?.user" class="review-actions">
|
||||||
<a-button
|
<a-button v-if="isCommentAuthor" @click="() => (isEditing = !isEditing)"> Edit </a-button>
|
||||||
v-if="isCommentAuthor"
|
|
||||||
@click="() => (isEditing = !isEditing)"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</a-button>
|
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
title="Are you sure you want to permanently delete this review?"
|
title="Are you sure you want to permanently delete this review?"
|
||||||
ok-text="Yes"
|
ok-text="Yes"
|
||||||
@ -64,13 +56,7 @@
|
|||||||
>
|
>
|
||||||
<a-button> Delete </a-button>
|
<a-button> Delete </a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<a-button
|
<a-button v-if="isAuthor" type="primary" @click="() => (replyFormVisible = !replyFormVisible)"> Reply </a-button>
|
||||||
v-if="isAuthor"
|
|
||||||
type="primary"
|
|
||||||
@click="() => (replyFormVisible = !replyFormVisible)"
|
|
||||||
>
|
|
||||||
Reply
|
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #author>
|
<template #author>
|
||||||
@ -91,25 +77,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<vee-form @submit="editSubmit" v-if="isEditing">
|
<vee-form @submit="editSubmit" v-if="isEditing">
|
||||||
<vee-field name="content" v-slot="{ value, field, errorMessage }">
|
<vee-field name="content" v-slot="{ value, field, errorMessage }">
|
||||||
<base-editor
|
<base-editor :val="review.text" :width="150" label="" :name="field.name" :init="comment" />
|
||||||
:val="review.text"
|
|
||||||
:width="150"
|
|
||||||
label=""
|
|
||||||
:name="field.name"
|
|
||||||
:init="comment"
|
|
||||||
/>
|
|
||||||
</vee-field>
|
</vee-field>
|
||||||
<a-button type="primary" html-type="submit"> Edit review </a-button>
|
<a-button type="primary" html-type="submit"> Edit review </a-button>
|
||||||
</vee-form>
|
</vee-form>
|
||||||
<div v-else v-html="review.text" />
|
<div v-else v-html="review.text" />
|
||||||
<vee-form @submit="replySubmit" v-if="replyFormVisible">
|
<vee-form @submit="replySubmit" v-if="replyFormVisible">
|
||||||
<vee-field name="content" v-slot="{ value, field, errorMessage }">
|
<vee-field name="content" v-slot="{ value, field, errorMessage }">
|
||||||
<base-editor
|
<base-editor :width="150" label="" :name="field.name" :init="comment" />
|
||||||
:width="150"
|
|
||||||
label=""
|
|
||||||
:name="field.name"
|
|
||||||
:init="comment"
|
|
||||||
/>
|
|
||||||
</vee-field>
|
</vee-field>
|
||||||
<a-button type="primary" html-type="submit"> Post response </a-button>
|
<a-button type="primary" html-type="submit"> Post response </a-button>
|
||||||
</vee-form>
|
</vee-form>
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
import { RuleExpression, useField } from "vee-validate";
|
import { RuleExpression, useField } from "vee-validate";
|
||||||
import { cs } from "@client/storyFormSchema";
|
import { cs } from "@client/storyFormSchema";
|
||||||
import { IBand } from "@models/band";
|
import { IBand } from "@models/band";
|
||||||
import { log } from "@server/logger";
|
|
||||||
|
|
||||||
import iconEl from "../icon.vue";
|
|
||||||
|
|
||||||
const bandlist = inject<IBand[]>("bandlist");
|
const bandlist = inject<IBand[]>("bandlist");
|
||||||
const fname = inject<string>("curName");
|
const fname = inject<string>("curName");
|
||||||
@ -19,20 +16,13 @@
|
|||||||
label: a.name,
|
label: a.name,
|
||||||
disabled: a.locked || false,
|
disabled: a.locked || false,
|
||||||
}));
|
}));
|
||||||
let bandField = useField(
|
let bandField = useField(fname + "bands", cs.fields.bands as unknown as MaybeRef<RuleExpression<number[]>>);
|
||||||
fname + "bands",
|
|
||||||
cs.fields.bands as unknown as MaybeRef<RuleExpression<number[]>>,
|
|
||||||
);
|
|
||||||
const { value, errorMessage, name, setValue } = bandField;
|
const { value, errorMessage, name, setValue } = bandField;
|
||||||
// setValue(sb)
|
// setValue(sb)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-form-item
|
<a-form-item :validate-status="!!errorMessage ? 'error' : undefined" :help="errorMessage as any" label="Bands">
|
||||||
:validate-status="!!errorMessage ? 'error' : undefined"
|
|
||||||
:help="errorMessage"
|
|
||||||
label="Bands"
|
|
||||||
>
|
|
||||||
<a-select
|
<a-select
|
||||||
:allow-clear="true"
|
:allow-clear="true"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { Field, ErrorMessage } from "vee-validate";
|
||||||
Form as VeeForm,
|
|
||||||
Field,
|
|
||||||
useForm,
|
|
||||||
useField,
|
|
||||||
ErrorMessage,
|
|
||||||
} from "vee-validate";
|
|
||||||
import { NamePath } from "ant-design-vue/es/form/interface";
|
import { NamePath } from "ant-design-vue/es/form/interface";
|
||||||
import { FormChapter } from "@client/types/form/story";
|
import { FormChapter } from "@client/types/form/story";
|
||||||
|
|
||||||
import { story, bare } from "@client/editorConfig";
|
import { bare } from "@client/editorConfig";
|
||||||
import elBands from "../atoms/bands.vue";
|
import elBands from "../atoms/bands.vue";
|
||||||
import genre from "../atoms/genre.vue";
|
import genre from "../atoms/genre.vue";
|
||||||
import elCharacters from "../atoms/characters.vue";
|
import elCharacters from "../atoms/characters.vue";
|
||||||
@ -41,16 +35,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<a-row :gutter="[10, 0]">
|
<a-row :gutter="[10, 0]">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<Field
|
<Field :name="name + '.chapterTitle'" v-slot="{ value, field, errorMessage }">
|
||||||
:name="name + '.chapterTitle'"
|
<a-form-item :name="[field.name as string]" label="Chapter title" :help="errorMessage" :status="!!errorMessage ? 'error' : undefined">
|
||||||
v-slot="{ value, field, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
:name="[field.name]"
|
|
||||||
label="Chapter title"
|
|
||||||
:help="errorMessage"
|
|
||||||
:status="!!errorMessage ? 'error' : undefined"
|
|
||||||
>
|
|
||||||
<a-input v-bind="field" />
|
<a-input v-bind="field" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</Field>
|
</Field>
|
||||||
@ -61,22 +47,10 @@
|
|||||||
</a-row>
|
</a-row>
|
||||||
<a-row :gutter="[10, 0]">
|
<a-row :gutter="[10, 0]">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<base-editor
|
<base-editor v-model:val="acData.summary" :name="name + '.summary'" :wrap-col="wrapc" label="Summary" :init="bare" />
|
||||||
v-model:val="acData.summary"
|
|
||||||
:name="name + '.summary'"
|
|
||||||
:wrap-col="wrapc"
|
|
||||||
label="Summary"
|
|
||||||
:init="bare"
|
|
||||||
/>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<base-editor
|
<base-editor v-model:val="acData.notes" :name="name + '.notes'" :wrap-col="wrapc" label="Author's notes" :init="bare" />
|
||||||
v-model:val="acData.notes"
|
|
||||||
:name="name + '.notes'"
|
|
||||||
:wrap-col="wrapc"
|
|
||||||
label="Author's notes"
|
|
||||||
:init="bare"
|
|
||||||
/>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row :gutter="[10, 0]">
|
<a-row :gutter="[10, 0]">
|
||||||
@ -87,39 +61,17 @@
|
|||||||
<el-pairings />
|
<el-pairings />
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<Field
|
<Field :name="name + '.nsfw'" type="checkbox" :unchecked-value="false" :value="true" v-slot="{ value, field, errorMessage }">
|
||||||
:name="name + '.nsfw'"
|
<a-checkbox v-bind="field" v-model="field.value"> Has NSFW content </a-checkbox>
|
||||||
type="checkbox"
|
|
||||||
:unchecked-value="false"
|
|
||||||
:value="true"
|
|
||||||
v-slot="{ value, field, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-checkbox v-bind="field" v-model="field.value">
|
|
||||||
Has NSFW content
|
|
||||||
</a-checkbox>
|
|
||||||
<error-message :name="field.name" />
|
<error-message :name="field.name" />
|
||||||
</Field>
|
</Field>
|
||||||
<Field
|
<Field :name="name + '.loggedInOnly'" type="checkbox" :unchecked-value="false" :value="true" v-slot="{ value, field, errorMessage }">
|
||||||
:name="name + '.loggedInOnly'"
|
|
||||||
type="checkbox"
|
|
||||||
:unchecked-value="false"
|
|
||||||
:value="true"
|
|
||||||
v-slot="{ value, field, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-checkbox v-bind="field"> Visible only to registered users </a-checkbox>
|
<a-checkbox v-bind="field"> Visible only to registered users </a-checkbox>
|
||||||
<error-message :name="field.name" />
|
<error-message :name="field.name" />
|
||||||
</Field>
|
</Field>
|
||||||
<Field
|
<Field :name="name + '.hidden'" type="checkbox" :unchecked-value="false" :value="true" v-slot="{ value, field, errorMessage }">
|
||||||
:name="name + '.hidden'"
|
|
||||||
type="checkbox"
|
|
||||||
:unchecked-value="false"
|
|
||||||
:value="true"
|
|
||||||
v-slot="{ value, field, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>
|
<template #title> Hides your story from everyone except you and site admins. </template>
|
||||||
Hides your story from everyone except you and site admins.
|
|
||||||
</template>
|
|
||||||
<a-checkbox v-bind="field"> Hidden </a-checkbox>
|
<a-checkbox v-bind="field"> Hidden </a-checkbox>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</Field>
|
</Field>
|
||||||
|
@ -2,25 +2,10 @@
|
|||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import lmove from "lodash-move";
|
import lmove from "lodash-move";
|
||||||
import {
|
import { Field, FieldArray, useForm } from "vee-validate";
|
||||||
Field,
|
|
||||||
Form as veeForm,
|
|
||||||
FieldArray,
|
|
||||||
FieldEntry,
|
|
||||||
useForm,
|
|
||||||
} from "vee-validate";
|
|
||||||
import { storySchema } from "@client/storyFormSchema";
|
import { storySchema } from "@client/storyFormSchema";
|
||||||
import {
|
import { FormStory, defaultChapter } from "@client/types/form/story";
|
||||||
FormChapter,
|
import { autoEdit, autoSave, debouncedAutoEdit, debouncedAutoSave } from "@client/utils";
|
||||||
FormStory,
|
|
||||||
defaultChapter,
|
|
||||||
} from "@client/types/form/story";
|
|
||||||
import {
|
|
||||||
autoEdit,
|
|
||||||
autoSave,
|
|
||||||
debouncedAutoEdit,
|
|
||||||
debouncedAutoSave,
|
|
||||||
} from "@client/utils";
|
|
||||||
|
|
||||||
import findUser from "~/components/findUser.vue";
|
import findUser from "~/components/findUser.vue";
|
||||||
|
|
||||||
@ -79,35 +64,17 @@
|
|||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
@invalid-submit="inval"
|
@invalid-submit="inval"
|
||||||
> -->
|
> -->
|
||||||
<form
|
<form @submit="subCb" @change="() => (canDraft ? debouncedAutoSave(values) : debouncedAutoEdit(values, endpoint, endpointMethod))">
|
||||||
@submit="subCb"
|
|
||||||
@change="
|
|
||||||
() =>
|
|
||||||
canDraft
|
|
||||||
? debouncedAutoSave(values)
|
|
||||||
: debouncedAutoEdit(values, endpoint, endpointMethod)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<!-- <a-form v-bind:model="acData"> -->
|
<!-- <a-form v-bind:model="acData"> -->
|
||||||
<Field name="title" v-slot="{ value, field, errorMessage }">
|
<Field name="title" v-slot="{ value, field, errorMessage }">
|
||||||
<a-form-item
|
<a-form-item label="Title" :help="errorMessage" :validate-status="!!errorMessage ? 'error' : ''">
|
||||||
label="Title"
|
|
||||||
:help="errorMessage"
|
|
||||||
:validate-status="!!errorMessage ? 'error' : ''"
|
|
||||||
>
|
|
||||||
<a-input v-bind="field" :value="value" />
|
<a-input v-bind="field" :value="value" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</Field>
|
</Field>
|
||||||
<a-form-item label="Co-author (optional)">
|
<a-form-item label="Co-author (optional)">
|
||||||
<find-user :initial-option="null" fieldName="coAuthor" :multi="false" />
|
<find-user :initial-option="null" fieldName="coAuthor" :multi="false" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<Field
|
<Field :unchecked-value="false" :value="true" type="checkbox" name="completed" v-slot="{ value, field, errorMessage }">
|
||||||
:unchecked-value="false"
|
|
||||||
:value="true"
|
|
||||||
type="checkbox"
|
|
||||||
name="completed"
|
|
||||||
v-slot="{ value, field, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-checkbox v-bind="field"> Complete </a-checkbox>
|
<a-checkbox v-bind="field"> Complete </a-checkbox>
|
||||||
</Field>
|
</Field>
|
||||||
<a-divider />
|
<a-divider />
|
||||||
@ -125,11 +92,7 @@
|
|||||||
if (e.moved) {
|
if (e.moved) {
|
||||||
// log.debug(e.moved);
|
// log.debug(e.moved);
|
||||||
move(e.moved.oldIndex, e.moved.newIndex);
|
move(e.moved.oldIndex, e.moved.newIndex);
|
||||||
acData.chapters = lmove(
|
acData.chapters = lmove(acData.chapters, e.moved.oldIndex, e.moved.newIndex);
|
||||||
acData.chapters,
|
|
||||||
e.moved.oldIndex,
|
|
||||||
e.moved.newIndex,
|
|
||||||
);
|
|
||||||
// log.debug(toRaw(acData.chapters.map((a) => toRaw(a))));
|
// log.debug(toRaw(acData.chapters.map((a) => toRaw(a))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,16 +157,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</field-array>
|
</field-array>
|
||||||
<a-button type="primary" html-type="submit">{{
|
<a-button type="primary" html-type="submit">{{ submitText || "Post" }}</a-button>
|
||||||
submitText || "Post"
|
<a-button html-type="submit" v-if="canDraft" @click="() => (otherBtnInvoked = true)"> Save for Later </a-button>
|
||||||
}}</a-button>
|
|
||||||
<a-button
|
|
||||||
html-type="submit"
|
|
||||||
v-if="canDraft"
|
|
||||||
@click="() => (otherBtnInvoked = true)"
|
|
||||||
>
|
|
||||||
Save for Later
|
|
||||||
</a-button>
|
|
||||||
</form>
|
</form>
|
||||||
<!-- </vee-form> -->
|
<!-- </vee-form> -->
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { useField } from "vee-validate";
|
||||||
Form as VeeForm,
|
|
||||||
Field as veeField,
|
|
||||||
useForm,
|
|
||||||
useField,
|
|
||||||
ErrorMessage,
|
|
||||||
} from "vee-validate";
|
|
||||||
import { story } from "@client/editorConfig";
|
import { story } from "@client/editorConfig";
|
||||||
import icon from "~/components/icon.vue";
|
import icon from "~/components/icon.vue";
|
||||||
import baseEditor from "../../baseEditor.vue";
|
import baseEditor from "../../baseEditor.vue";
|
||||||
@ -22,12 +16,7 @@
|
|||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<base-editor
|
<base-editor label="" v-if="pvalue === 'pasteOrType'" :init="story" :name="fname + 'content'" />
|
||||||
label=""
|
|
||||||
v-if="pvalue === 'pasteOrType'"
|
|
||||||
:init="story"
|
|
||||||
:name="fname + 'content'"
|
|
||||||
/>
|
|
||||||
<a-upload
|
<a-upload
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
v-else-if="pvalue === 'upload'"
|
v-else-if="pvalue === 'upload'"
|
||||||
@ -53,10 +42,6 @@
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-button type="primary">
|
<a-button type="primary"> <icon istyle="regular" name="upload" /><span style="margin-left: 0.5em"> Upload a file</span> </a-button>
|
||||||
<icon istyle="regular" name="upload" /><span style="margin-left: 0.5em">
|
|
||||||
Upload a file</span
|
|
||||||
>
|
|
||||||
</a-button>
|
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { AsyncData, UseFetchOptions } from "#app";
|
import { AsyncData } from "#app";
|
||||||
import { NitroFetchRequest } from "nitropack";
|
|
||||||
|
|
||||||
const useApiFetch = async <T>(url: string, options?: any) => {
|
const useApiFetch = async <T>(url: string, options?: any) => {
|
||||||
// const { token } = useAuth();
|
// const { token } = useAuth();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import cfooter from "~/components/layouts/footer.vue";
|
import cfooter from "~/components/layouts/footer.vue";
|
||||||
import sidebarThing from "~/components/layouts/sidebar.vue";
|
import sidebarThing from "~/components/layouts/sidebar.vue";
|
||||||
import icon from "~/components/icon.vue";
|
import icon from "~/components/icon.vue";
|
||||||
import { ISidebarItem } from "@models/sidebarEntry";
|
|
||||||
const { useToken } = theme;
|
const { useToken } = theme;
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
|
|
||||||
@ -52,10 +52,7 @@
|
|||||||
<div class="stat-block">
|
<div class="stat-block">
|
||||||
<div>
|
<div>
|
||||||
<a-typography-text> Band fiction that rocks </a-typography-text>
|
<a-typography-text> Band fiction that rocks </a-typography-text>
|
||||||
<a-typography-text type="secondary">
|
<a-typography-text type="secondary"> With {{ totals?.stories ?? 0 }} stories by {{ totals?.authors ?? 0 }} authors </a-typography-text>
|
||||||
With {{ totals?.stories ?? 0 }} stories by
|
|
||||||
{{ totals?.authors ?? 0 }} authors
|
|
||||||
</a-typography-text>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<navbar />
|
<navbar />
|
||||||
|
@ -1,39 +1,20 @@
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { FormChapter, FormStory } from "./types/form/story";
|
import { FormChapter } from "./types/form/story";
|
||||||
|
|
||||||
const emptySummary = "Summary cannot be blank";
|
const emptySummary = "Summary cannot be blank";
|
||||||
const emptyChapterTitle = "Chapter title cannot be blank.";
|
const emptyChapterTitle = "Chapter title cannot be blank.";
|
||||||
const blankTitle = "Title cannot be blank.";
|
const blankTitle = "Title cannot be blank.";
|
||||||
|
|
||||||
export const cs = yup.object<FormChapter>().shape({
|
export const cs = yup.object<FormChapter>().shape({
|
||||||
chapterTitle: yup
|
chapterTitle: yup.string().ensure().min(1, emptyChapterTitle).trim(emptyChapterTitle).required(emptyChapterTitle),
|
||||||
.string()
|
|
||||||
.ensure()
|
|
||||||
.min(1, emptyChapterTitle)
|
|
||||||
.trim(emptyChapterTitle)
|
|
||||||
.required(emptyChapterTitle),
|
|
||||||
summary: yup.string().ensure().min(10, emptySummary).required(emptySummary),
|
summary: yup.string().ensure().min(10, emptySummary).required(emptySummary),
|
||||||
notes: yup.string().ensure(),
|
notes: yup.string().ensure(),
|
||||||
bands: yup
|
bands: yup.array().ensure().of(yup.number()).min(1, "One or more bands must be selected."),
|
||||||
.array()
|
characters: yup.array().ensure().min(1, "One or more characters must be selected"),
|
||||||
.ensure()
|
|
||||||
.of(yup.number())
|
|
||||||
.min(1, "One or more bands must be selected."),
|
|
||||||
characters: yup
|
|
||||||
.array()
|
|
||||||
.ensure()
|
|
||||||
.min(1, "One or more characters must be selected"),
|
|
||||||
relationships: yup
|
relationships: yup
|
||||||
.array()
|
.array()
|
||||||
.ensure()
|
.ensure()
|
||||||
.of(
|
.of(yup.array().ensure().of(yup.string()).min(2, "Pairings must have at least two characters!").max(3, "Pairings can have no more than three characters!")),
|
||||||
yup
|
|
||||||
.array()
|
|
||||||
.ensure()
|
|
||||||
.of(yup.string())
|
|
||||||
.min(2, "Pairings must have at least two characters!")
|
|
||||||
.max(3, "Pairings can have no more than three characters!"),
|
|
||||||
),
|
|
||||||
nsfw: yup.boolean().oneOf([true, false]),
|
nsfw: yup.boolean().oneOf([true, false]),
|
||||||
loggedInOnly: yup.boolean().when("nsfw", ([nsfw], schema) => {
|
loggedInOnly: yup.boolean().when("nsfw", ([nsfw], schema) => {
|
||||||
return nsfw
|
return nsfw
|
||||||
@ -44,20 +25,13 @@ export const cs = yup.object<FormChapter>().shape({
|
|||||||
: schema.oneOf([true, false]);
|
: schema.oneOf([true, false]);
|
||||||
}),
|
}),
|
||||||
hidden: yup.boolean().oneOf([true, false]),
|
hidden: yup.boolean().oneOf([true, false]),
|
||||||
pot: yup
|
pot: yup.string().oneOf(["pasteOrType", "upload"]).required("Story content is required!"),
|
||||||
.string()
|
|
||||||
.oneOf(["pasteOrType", "upload"])
|
|
||||||
.required("Story content is required!"),
|
|
||||||
storytext: yup.string().when("pot", ([pot], schema) => {
|
storytext: yup.string().when("pot", ([pot], schema) => {
|
||||||
return pot === "pasteOrType"
|
return pot === "pasteOrType"
|
||||||
? schema
|
? schema
|
||||||
.test(
|
.test("numWords", "Story must be at least 50 words", (value: any, context) => {
|
||||||
"numWords",
|
return value?.split(/\W+/).length > 50 || false;
|
||||||
"Story must be at least 50 words",
|
})
|
||||||
(value: any, context) => {
|
|
||||||
return value?.split(/\W+/).length > 50 || false;
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.required("Story text can't be blank!")
|
.required("Story text can't be blank!")
|
||||||
: schema.min(0);
|
: schema.min(0);
|
||||||
}),
|
}),
|
||||||
@ -98,10 +72,6 @@ export const cs = yup.object<FormChapter>().shape({
|
|||||||
|
|
||||||
export const storySchema = yup.object().shape({
|
export const storySchema = yup.object().shape({
|
||||||
title: yup.string().ensure().min(5, blankTitle).required(blankTitle),
|
title: yup.string().ensure().min(5, blankTitle).required(blankTitle),
|
||||||
chapters: yup
|
chapters: yup.array().min(1, "There must be at least one chapter.").of(cs).ensure(),
|
||||||
.array()
|
|
||||||
.min(1, "There must be at least one chapter.")
|
|
||||||
.of(cs)
|
|
||||||
.ensure(),
|
|
||||||
completed: yup.boolean().oneOf([true, false]),
|
completed: yup.boolean().oneOf([true, false]),
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { readFileSync } from "fs";
|
|
||||||
import { resolve } from "path";
|
|
||||||
// import chardet from "chardet";
|
// import chardet from "chardet";
|
||||||
// import iconv from "iconv-lite";
|
// import iconv from "iconv-lite";
|
||||||
import { GridFSBucketReadStream } from "mongodb";
|
import { GridFSBucketReadStream } from "mongodb";
|
||||||
@ -15,20 +13,13 @@ export function countWords(string: string) {
|
|||||||
return stripHtml(string).result.split(/W+/).length;
|
return stripHtml(string).result.split(/W+/).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function populate<T>(
|
export function populate<T>(field: string, model: string): PreMiddlewareFunction<Query<T, T>> {
|
||||||
field: string,
|
|
||||||
model: string,
|
|
||||||
): PreMiddlewareFunction<Query<T, T>> {
|
|
||||||
return function (next: () => any) {
|
return function (next: () => any) {
|
||||||
this.populate(field, undefined, model);
|
this.populate(field, undefined, model);
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function populateSelected<T>(
|
export function populateSelected<T>(field: string, model: string, selection: string): PreMiddlewareFunction<Query<T, T>> {
|
||||||
field: string,
|
|
||||||
model: string,
|
|
||||||
selection: string,
|
|
||||||
): PreMiddlewareFunction<Query<T, T>> {
|
|
||||||
return function (next: () => any) {
|
return function (next: () => any) {
|
||||||
this.populate(field, selection, model);
|
this.populate(field, selection, model);
|
||||||
next();
|
next();
|
||||||
@ -40,15 +31,11 @@ export function isFicmasHidden(story: IStory): boolean {
|
|||||||
((story.ficmas as IFicmas)?.year == new Date().getFullYear() &&
|
((story.ficmas as IFicmas)?.year == new Date().getFullYear() &&
|
||||||
(story.ficmas as IFicmas)?.anniversary &&
|
(story.ficmas as IFicmas)?.anniversary &&
|
||||||
new Date() < new Date(Date.parse("Aug 1 " + new Date().getFullYear()))) ||
|
new Date() < new Date(Date.parse("Aug 1 " + new Date().getFullYear()))) ||
|
||||||
((story.ficmas as IFicmas)?.year == new Date().getFullYear() &&
|
((story.ficmas as IFicmas)?.year == new Date().getFullYear() && !(story.ficmas as IFicmas)?.anniversary && ficsHidden(Date.now()))
|
||||||
!(story.ficmas as IFicmas)?.anniversary &&
|
|
||||||
ficsHidden(Date.now()))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringifyStream(
|
export function stringifyStream(stream: GridFSBucketReadStream): Promise<string> {
|
||||||
stream: GridFSBucketReadStream,
|
|
||||||
): Promise<string> {
|
|
||||||
let chunks: Buffer[] = [];
|
let chunks: Buffer[] = [];
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
stream.on("data", (c) => chunks.push(Buffer.from(c)));
|
stream.on("data", (c) => chunks.push(Buffer.from(c)));
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { H3Event, EventHandlerRequest } from "h3";
|
import { H3Event, EventHandlerRequest } from "h3";
|
||||||
import { GridFSBucket } from "mongodb";
|
import { Document } from "mongoose";
|
||||||
import mongoose, { Document } from "mongoose";
|
|
||||||
import { norm, stringifyStream } from "@functions";
|
import { norm, stringifyStream } from "@functions";
|
||||||
import { IStory } from "@models/stories";
|
import { IStory } from "@models/stories";
|
||||||
import { IChapter } from "@models/stories/chapter";
|
import { IChapter } from "@models/stories/chapter";
|
||||||
@ -16,11 +15,7 @@ export default async function (
|
|||||||
const cloned: any & { chapters: IChapter[] } = { ...finObj };
|
const cloned: any & { chapters: IChapter[] } = { ...finObj };
|
||||||
delete finObj.chapters;
|
delete finObj.chapters;
|
||||||
const bucket = getBucket();
|
const bucket = getBucket();
|
||||||
let ds = bucket.openDownloadStreamByName(
|
let ds = bucket.openDownloadStreamByName(`/stories/${cloned.chapters[cindex || event.context.chapterIndex || 0].id}.txt`);
|
||||||
`/stories/${
|
|
||||||
cloned.chapters[cindex || event.context.chapterIndex || 0].id
|
|
||||||
}.txt`,
|
|
||||||
);
|
|
||||||
let stream = await stringifyStream(ds);
|
let stream = await stringifyStream(ds);
|
||||||
finObj.currentChapter = {
|
finObj.currentChapter = {
|
||||||
...cloned.chapters[cindex || event.context.chapterIndex || 0],
|
...cloned.chapters[cindex || event.context.chapterIndex || 0],
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Band } from "@models/band";
|
|
||||||
import { Challenge } from "@models/challenges/gen";
|
import { Challenge } from "@models/challenges/gen";
|
||||||
import { IStory, Story } from "@models/stories";
|
import { IStory, Story } from "@models/stories";
|
||||||
import { log } from "../logger";
|
import { log } from "../logger";
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import { H3Event, EventHandlerRequest } from "h3";
|
|
||||||
import { apiRoot } from "./constants";
|
import { apiRoot } from "./constants";
|
||||||
export default async function (id: number): Promise<number> {
|
export default async function (id: number): Promise<number> {
|
||||||
let { data: lookup } = await useFetch<any>(
|
let { data: lookup } = await useFetch<any>(`${apiRoot}/session-sharing/lookup`, {
|
||||||
`${apiRoot}/session-sharing/lookup`,
|
method: "get",
|
||||||
{
|
query: {
|
||||||
method: "get",
|
params: {
|
||||||
query: {
|
id,
|
||||||
params: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
return lookup.value.uid as number;
|
return lookup.value.uid as number;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import winston from "winston";
|
import winston from "winston";
|
||||||
const { combine, timestamp, json, splat, printf, colorize } = winston.format;
|
const { combine, timestamp, json, splat, printf, colorize } = winston.format;
|
||||||
|
|
||||||
winston.add;
|
// winston.add;
|
||||||
|
|
||||||
const fmt = printf(({ timestamp, level, message, label, durationMs }) => {
|
const fmt = printf(({ timestamp, level, message, label, durationMs }) => {
|
||||||
return `${timestamp} [${label || "misc"}] ${message} ${
|
return `${timestamp} [${label || "misc"}] ${message} ${!!durationMs ? " (took " + durationMs + "ms)" : ""}`;
|
||||||
!!durationMs ? " (took " + durationMs + "ms)" : ""
|
|
||||||
}`;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cfmt = combine(json(), timestamp(), fmt);
|
const cfmt = combine(json(), timestamp(), fmt);
|
||||||
|
@ -2,7 +2,6 @@ import { EventHandlerRequest, H3Event } from "h3";
|
|||||||
import { messages } from "@server/constants";
|
import { messages } from "@server/constants";
|
||||||
import { IStory } from "@models/stories";
|
import { IStory } from "@models/stories";
|
||||||
import { isFicmasHidden } from "@functions";
|
import { isFicmasHidden } from "@functions";
|
||||||
import { IDraft } from "@models/stories/draft";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
export function isIdNan(ev: H3Event<EventHandlerRequest>) {
|
export function isIdNan(ev: H3Event<EventHandlerRequest>) {
|
||||||
@ -33,11 +32,7 @@ export function isLoggedIn(ev: H3Event<EventHandlerRequest>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function storyCheck(
|
export async function storyCheck(event: H3Event<EventHandlerRequest>, story: IStory, idx: number) {
|
||||||
event: H3Event<EventHandlerRequest>,
|
|
||||||
story: IStory,
|
|
||||||
idx: number,
|
|
||||||
) {
|
|
||||||
let ret: any = {};
|
let ret: any = {};
|
||||||
if (!story) {
|
if (!story) {
|
||||||
ret.statusCode = 404;
|
ret.statusCode = 404;
|
||||||
@ -49,11 +44,7 @@ export async function storyCheck(
|
|||||||
message: `TOP SECRET! This story is part of an ongoing challenge. You'll be able to read it after the challenge's reveal date.`,
|
message: `TOP SECRET! This story is part of an ongoing challenge. You'll be able to read it after the challenge's reveal date.`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (story.chapters[idx]?.hidden && event.context.currentUser?._id !== (story.author as IUser)._id && !event.context.currentUser?.profile.isAdmin) {
|
||||||
story.chapters[idx]?.hidden &&
|
|
||||||
event.context.currentUser?._id !== (story.author as IUser)._id &&
|
|
||||||
!event.context.currentUser?.profile.isAdmin
|
|
||||||
) {
|
|
||||||
ret.statusCode = 403;
|
ret.statusCode = 403;
|
||||||
ret.message = messages[403];
|
ret.message = messages[403];
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { connect, Document, Model } from "mongoose";
|
import mongoose, { Model } from "mongoose";
|
||||||
const { Schema, model } = mongoose;
|
const { Schema, model } = mongoose;
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
@ -30,11 +30,5 @@ const BandSchema = new mongoose.Schema<IBand>({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Band && BandSchema.plugin(AutoIncrement, { id: "band" });
|
||||||
!mongoose.models.Band &&
|
export const Band: Model<IBand> = /* mongoose.models.Band || */ model<IBand>("Band", BandSchema, "bands");
|
||||||
BandSchema.plugin(AutoIncrement, { id: "band" });
|
|
||||||
export const Band: Model<IBand> = /* mongoose.models.Band || */ model<IBand>(
|
|
||||||
"Band",
|
|
||||||
BandSchema,
|
|
||||||
"bands",
|
|
||||||
);
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { PopulatedDoc, Model } from "mongoose";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
@ -67,8 +67,5 @@ const biffnoschema = new mongoose.Schema<IBiffno>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Biffno && biffnoschema.plugin(AutoIncrement, { start_seq: 1, id: "bif_id" });
|
||||||
!mongoose.models.Biffno &&
|
export const Biffno: Model<IBiffno> = mongoose.models.Biffno || mongoose.model("Biffno", biffnoschema, "biffno");
|
||||||
biffnoschema.plugin(AutoIncrement, { start_seq: 1, id: "bif_id" });
|
|
||||||
export const Biffno: Model<IBiffno> =
|
|
||||||
mongoose.models.Biffno || mongoose.model("Biffno", biffnoschema, "biffno");
|
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import mongoose, {
|
import mongoose, { PopulatedDoc, Model, model } from "mongoose";
|
||||||
Schema,
|
|
||||||
PopulatedDoc,
|
|
||||||
Document,
|
|
||||||
Model,
|
|
||||||
model,
|
|
||||||
} from "mongoose";
|
|
||||||
|
|
||||||
import { IBand } from "@models/band";
|
import { IBand } from "@models/band";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
@ -48,9 +42,6 @@ export const FicmasSchema = new mongoose.Schema<IFicmas>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Ficmas && FicmasSchema.plugin(AutoIncrement, { id: "ficmas_wishes", inc_field: "_id" });
|
||||||
!mongoose.models.Ficmas &&
|
|
||||||
FicmasSchema.plugin(AutoIncrement, { id: "ficmas_wishes", inc_field: "_id" });
|
|
||||||
|
|
||||||
export const Ficmas: Model<IFicmas> =
|
export const Ficmas: Model<IFicmas> = mongoose.models.Ficmas || model("Ficmas", FicmasSchema, "ficmas_wishes");
|
||||||
mongoose.models.Ficmas || model("Ficmas", FicmasSchema, "ficmas_wishes");
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { Model } from "mongoose";
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
|
|
||||||
@ -45,9 +45,7 @@ const challengeSchema = new mongoose.Schema<IChallenge>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Challenge && challengeSchema.plugin(AutoIncrement, { id: "challenges" });
|
||||||
!mongoose.models.Challenge &&
|
|
||||||
challengeSchema.plugin(AutoIncrement, { id: "challenges" });
|
|
||||||
export const Challenge: Model<IChallenge> =
|
export const Challenge: Model<IChallenge> =
|
||||||
// mongoose.models.Challenge ||
|
// mongoose.models.Challenge ||
|
||||||
mongoose.model("Challenge", challengeSchema, "challenges");
|
mongoose.model("Challenge", challengeSchema, "challenges");
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
import mongoose, {
|
import mongoose, { Schema, PopulatedDoc, Model } from "mongoose";
|
||||||
Schema,
|
|
||||||
connect,
|
|
||||||
PopulatedDoc,
|
|
||||||
Document,
|
|
||||||
Model,
|
|
||||||
} from "mongoose";
|
|
||||||
import SequenceFactory from "mongoose-sequence";
|
|
||||||
|
|
||||||
import { IPrivMsg } from "./privMsg";
|
import { IPrivMsg } from "./privMsg";
|
||||||
import { IUser } from "./user";
|
import { IUser } from "./user";
|
||||||
@ -41,8 +34,4 @@ const InboxSchema = new Schema<IInbox>({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Inbox: Model<IInbox> = mongoose.model<IInbox>(
|
export const Inbox: Model<IInbox> = mongoose.model<IInbox>("Inbox", InboxSchema, "inboxes");
|
||||||
"Inbox",
|
|
||||||
InboxSchema,
|
|
||||||
"inboxes",
|
|
||||||
);
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { PopulatedDoc, Model } from "mongoose";
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
import { IUser } from "./user";
|
import { IUser } from "./user";
|
||||||
@ -50,13 +50,6 @@ const PMSchema = new mongoose.Schema<IPrivMsg>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.PrivMsg && PMSchema.plugin(AutoIncrement, { id: "private_message" });
|
||||||
!mongoose.models.PrivMsg &&
|
|
||||||
PMSchema.plugin(AutoIncrement, { id: "private_message" });
|
|
||||||
|
|
||||||
export const PrivMsg: Model<IPrivMsg> =
|
export const PrivMsg: Model<IPrivMsg> = /* mongoose.models.PrivMsg || */ mongoose.model("PrivMsg", PMSchema, "private_messages");
|
||||||
/* mongoose.models.PrivMsg || */ mongoose.model(
|
|
||||||
"PrivMsg",
|
|
||||||
PMSchema,
|
|
||||||
"private_messages",
|
|
||||||
);
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { connect, PopulatedDoc, Document } from "mongoose";
|
import mongoose from "mongoose";
|
||||||
const { Schema, model } = mongoose;
|
const { Schema, model } = mongoose;
|
||||||
|
|
||||||
interface IAbstractQM {
|
interface IAbstractQM {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { Model } from "mongoose";
|
||||||
|
|
||||||
export enum Color {
|
export enum Color {
|
||||||
"orange" = "orange",
|
"orange" = "orange",
|
||||||
@ -35,9 +35,4 @@ const SISchema = new mongoose.Schema<ISidebarItem>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const SidebarItem: Model<ISidebarItem> =
|
export const SidebarItem: Model<ISidebarItem> =
|
||||||
mongoose.models.SidebarItem ||
|
mongoose.models.SidebarItem || /* mongoose.models.SidebarItem || */ mongoose.model("SidebarItem", SISchema, "sidebar");
|
||||||
/* mongoose.models.SidebarItem || */ mongoose.model(
|
|
||||||
"SidebarItem",
|
|
||||||
SISchema,
|
|
||||||
"sidebar",
|
|
||||||
);
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import { Schema, PopulatedDoc } from "mongoose";
|
||||||
import { IBand } from "@models/band";
|
import { IBand } from "@models/band";
|
||||||
export interface IChapter {
|
export interface IChapter {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -1,19 +1,12 @@
|
|||||||
import { IStory } from ".";
|
import { IStory } from ".";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
import { IBand } from "@models/band";
|
import mongoose, { Schema, Model } from "mongoose";
|
||||||
import { IFicmas } from "@models/challenges/ficmas";
|
|
||||||
import { IChallenge } from "@models/challenges/gen";
|
|
||||||
import { IUser } from "@models/user";
|
|
||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { Chapter } from "./chapter";
|
import { Chapter } from "./chapter";
|
||||||
|
|
||||||
const AutoIncrement = SequenceFactory(mongoose);
|
const AutoIncrement = SequenceFactory(mongoose);
|
||||||
|
|
||||||
export type IDraft = Omit<
|
export type IDraft = Omit<IStory, "recs" | "favs" | "reviews" | "views" | "downloads" | "posted">;
|
||||||
IStory,
|
|
||||||
"recs" | "favs" | "reviews" | "views" | "downloads" | "posted"
|
|
||||||
>;
|
|
||||||
|
|
||||||
// const Cha
|
// const Cha
|
||||||
|
|
||||||
@ -39,9 +32,6 @@ const DraftSchema = new Schema<IDraft>(
|
|||||||
{ timestamps: true },
|
{ timestamps: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Draft && DraftSchema.plugin(AutoIncrement, { id: "drafts" });
|
||||||
!mongoose.models.Draft &&
|
|
||||||
DraftSchema.plugin(AutoIncrement, { id: "drafts" });
|
|
||||||
|
|
||||||
export const Draft: Model<IDraft> =
|
export const Draft: Model<IDraft> = /* mongoose.models.Draft || */ mongoose.model("Draft", DraftSchema, "drafts");
|
||||||
/* mongoose.models.Draft || */ mongoose.model("Draft", DraftSchema, "drafts");
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { PopulatedDoc, Model } from "mongoose";
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
|
|
||||||
const AutoIncrement = SequenceFactory(mongoose);
|
const AutoIncrement = SequenceFactory(mongoose);
|
||||||
@ -87,13 +87,6 @@ const StorySchema = new mongoose.Schema<IStory>({
|
|||||||
default: new Date(),
|
default: new Date(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Story && Chapter.plugin(AutoIncrement, { id: "chapterid", inc_field: "id" });
|
||||||
!mongoose.models.Story &&
|
|
||||||
Chapter.plugin(AutoIncrement, { id: "chapterid", inc_field: "id" });
|
|
||||||
hasMigrated && StorySchema.plugin(AutoIncrement, { id: "storyid" });
|
hasMigrated && StorySchema.plugin(AutoIncrement, { id: "storyid" });
|
||||||
export const Story: Model<IStory> =
|
export const Story: Model<IStory> = /* mongoose.models.Story || */ mongoose.model("Story", StorySchema, "stories");
|
||||||
/* mongoose.models.Story || */ mongoose.model(
|
|
||||||
"Story",
|
|
||||||
StorySchema,
|
|
||||||
"stories",
|
|
||||||
);
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
|
import mongoose, { PopulatedDoc, Model } from "mongoose";
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import { hasMigrated } from "@dbconfig";
|
import { hasMigrated } from "@dbconfig";
|
||||||
import { populate, populateSelected } from "@functions";
|
import { populate, populateSelected } from "@functions";
|
||||||
@ -63,22 +63,9 @@ CommentSchema
|
|||||||
// .pre("find", populateSelected("replyingTo", modelName, "-replies -author"))
|
// .pre("find", populateSelected("replyingTo", modelName, "-replies -author"))
|
||||||
.pre("findOne", populate("replies", modelName))
|
.pre("findOne", populate("replies", modelName))
|
||||||
.pre("find", populate("replies", modelName))
|
.pre("find", populate("replies", modelName))
|
||||||
.pre(
|
.pre("findOne", populateSelected("author", "User", "profile username _id blocked"))
|
||||||
"findOne",
|
.pre("find", populateSelected("author", "User", "profile username _id blocked"));
|
||||||
populateSelected("author", "User", "profile username _id blocked"),
|
|
||||||
)
|
|
||||||
.pre(
|
|
||||||
"find",
|
|
||||||
populateSelected("author", "User", "profile username _id blocked"),
|
|
||||||
);
|
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.Review && CommentSchema.plugin(AutoIncrement, { id: "reviews" });
|
||||||
!mongoose.models.Review &&
|
|
||||||
CommentSchema.plugin(AutoIncrement, { id: "reviews" });
|
|
||||||
|
|
||||||
export const Review: Model<IReview> =
|
export const Review: Model<IReview> = /* mongoose.models.Review || */ mongoose.model(modelName, CommentSchema, "reviews");
|
||||||
/* mongoose.models.Review || */ mongoose.model(
|
|
||||||
modelName,
|
|
||||||
CommentSchema,
|
|
||||||
"reviews",
|
|
||||||
);
|
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import mongoose, {
|
import mongoose, { PopulatedDoc, Document, Model } from "mongoose";
|
||||||
Schema,
|
|
||||||
connect,
|
|
||||||
PopulatedDoc,
|
|
||||||
Document,
|
|
||||||
Model,
|
|
||||||
} from "mongoose";
|
|
||||||
import SequenceFactory from "mongoose-sequence";
|
import SequenceFactory from "mongoose-sequence";
|
||||||
import bcrypt from "bcryptjs";
|
import bcrypt from "bcryptjs";
|
||||||
import md5 from "blueimp-md5";
|
import md5 from "blueimp-md5";
|
||||||
@ -274,25 +268,15 @@ UserSchema.static("generateHash", function (password: string): string {
|
|||||||
return bcrypt.hashSync(password, bcrypt.genSaltSync(8));
|
return bcrypt.hashSync(password, bcrypt.genSaltSync(8));
|
||||||
});
|
});
|
||||||
UserSchema.methods.validPassword = function (password: string): boolean {
|
UserSchema.methods.validPassword = function (password: string): boolean {
|
||||||
return (
|
return md5(password) === this.password || bcrypt.compareSync(password, this.password) || false;
|
||||||
md5(password) === this.password ||
|
|
||||||
bcrypt.compareSync(password, this.password) ||
|
|
||||||
false
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
UserSchema.methods.generateJWT = function (jwtKey: string): string {
|
UserSchema.methods.generateJWT = function (jwtKey: string): string {
|
||||||
let token = jwt.sign(
|
let token = jwt.sign({ id: this._id, isAdmin: this.profile.isAdmin }, jwtKey, {
|
||||||
{ id: this._id, isAdmin: this.profile.isAdmin },
|
expiresIn: "14 days",
|
||||||
jwtKey,
|
});
|
||||||
{
|
|
||||||
expiresIn: "14 days",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return token;
|
return token;
|
||||||
};
|
};
|
||||||
|
|
||||||
hasMigrated &&
|
hasMigrated && !mongoose.models.User && UserSchema.plugin(AutoIncrement, { id: "userid", inc_field: "_id" });
|
||||||
!mongoose.models.User &&
|
|
||||||
UserSchema.plugin(AutoIncrement, { id: "userid", inc_field: "_id" });
|
|
||||||
export const User = mongoose.model<IUser, UModel>("User", UserSchema, "users");
|
export const User = mongoose.model<IUser, UModel>("User", UserSchema, "users");
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive } from "vue";
|
import { reactive } from "vue";
|
||||||
import useApiFetch from "../../composables/useApiFetch";
|
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
interface FormState {
|
interface FormState {
|
||||||
username: string;
|
username: string;
|
||||||
@ -38,28 +37,11 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a-form
|
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" autocomplete="off" :colon="false" layout="vertical" @finish="onFinish">
|
||||||
:model="formState"
|
<a-form-item label="Username" name="username" :rules="[{ required: true, message: 'Username required!' }]">
|
||||||
name="basic"
|
|
||||||
:label-col="{ span: 8 }"
|
|
||||||
autocomplete="off"
|
|
||||||
:colon="false"
|
|
||||||
layout="vertical"
|
|
||||||
@finish="onFinish"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
label="Username"
|
|
||||||
name="username"
|
|
||||||
:rules="[{ required: true, message: 'Username required!' }]"
|
|
||||||
>
|
|
||||||
<a-input v-model:value="formState.username" />
|
<a-input v-model:value="formState.username" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item :colon="false" label="Password" name="password" :rules="[{ required: true, message: 'Password required!' }]">
|
||||||
:colon="false"
|
|
||||||
label="Password"
|
|
||||||
name="password"
|
|
||||||
:rules="[{ required: true, message: 'Password required!' }]"
|
|
||||||
>
|
|
||||||
<a-input-password v-model:value="formState.password" />
|
<a-input-password v-model:value="formState.password" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
|
@ -1,21 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive } from "vue";
|
import { Field as veeField, useField, useForm } from "vee-validate";
|
||||||
import {
|
|
||||||
Form as veeForm,
|
|
||||||
Field as veeField,
|
|
||||||
useField,
|
|
||||||
useForm,
|
|
||||||
useSetFieldValue,
|
|
||||||
useFormErrors,
|
|
||||||
useFormValues,
|
|
||||||
RuleExpression,
|
|
||||||
} from "vee-validate";
|
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { useRecaptchaProvider } from "vue-recaptcha";
|
import { useRecaptchaProvider } from "vue-recaptcha";
|
||||||
|
|
||||||
import { useChallengeV2 } from "vue-recaptcha";
|
import { useChallengeV2 } from "vue-recaptcha";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { log } from "@server/logger";
|
|
||||||
import termsOfServices from "~/components/tos.vue";
|
import termsOfServices from "~/components/tos.vue";
|
||||||
useRecaptchaProvider();
|
useRecaptchaProvider();
|
||||||
|
|
||||||
@ -34,24 +23,11 @@
|
|||||||
});
|
});
|
||||||
// const { execute } = useChallengeV3('submit');
|
// const { execute } = useChallengeV3('submit');
|
||||||
const vschema = yup.object<FormState>().shape({
|
const vschema = yup.object<FormState>().shape({
|
||||||
username: yup
|
username: yup.string().ensure().trim().min(1).required("Username is required!"),
|
||||||
.string()
|
password: yup.string().ensure().trim().min(8).required("Password is required!"),
|
||||||
.ensure()
|
|
||||||
.trim()
|
|
||||||
.min(1)
|
|
||||||
.required("Username is required!"),
|
|
||||||
password: yup
|
|
||||||
.string()
|
|
||||||
.ensure()
|
|
||||||
.trim()
|
|
||||||
.min(8)
|
|
||||||
.required("Password is required!"),
|
|
||||||
email: yup.string().ensure().trim().email().required("Email is required!"),
|
email: yup.string().ensure().trim().email().required("Email is required!"),
|
||||||
// recaptcha: yup.string().required('Please verify you are human.'),
|
// recaptcha: yup.string().required('Please verify you are human.'),
|
||||||
agree: yup
|
agree: yup.boolean().oneOf([true], "Please agree to the terms.").required("Please agree to the terms."),
|
||||||
.boolean()
|
|
||||||
.oneOf([true], "Please agree to the terms.")
|
|
||||||
.required("Please agree to the terms."),
|
|
||||||
});
|
});
|
||||||
const dv: FormState = {
|
const dv: FormState = {
|
||||||
username: "",
|
username: "",
|
||||||
@ -120,48 +96,26 @@ notification[ "error" ]({
|
|||||||
<template>
|
<template>
|
||||||
<form @submit="onFinish">
|
<form @submit="onFinish">
|
||||||
<vee-field name="username" v-slot="{ field, errorMessage, value }">
|
<vee-field name="username" v-slot="{ field, errorMessage, value }">
|
||||||
<a-form-item
|
<a-form-item label="Username" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
|
||||||
label="Username"
|
|
||||||
:name="field.name"
|
|
||||||
:validate-status="!!errorMessage ? 'error' : ''"
|
|
||||||
:help="errorMessage"
|
|
||||||
>
|
|
||||||
<a-input v-bind="field" v-model:value="field.value" />
|
<a-input v-bind="field" v-model:value="field.value" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</vee-field>
|
</vee-field>
|
||||||
<vee-field name="email" v-slot="{ field, errorMessage, value }">
|
<vee-field name="email" v-slot="{ field, errorMessage, value }">
|
||||||
<a-form-item
|
<a-form-item label="Email address" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
|
||||||
label="Email address"
|
|
||||||
:name="field.name"
|
|
||||||
:validate-status="!!errorMessage ? 'error' : ''"
|
|
||||||
:help="errorMessage"
|
|
||||||
>
|
|
||||||
<a-input v-bind="field" v-model:value="field.value" />
|
<a-input v-bind="field" v-model:value="field.value" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</vee-field>
|
</vee-field>
|
||||||
<vee-field name="password" v-slot="{ field, errorMessage, value }">
|
<vee-field name="password" v-slot="{ field, errorMessage, value }">
|
||||||
<a-form-item
|
<a-form-item label="Password" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
|
||||||
label="Password"
|
|
||||||
:name="field.name"
|
|
||||||
:validate-status="!!errorMessage ? 'error' : ''"
|
|
||||||
:help="errorMessage"
|
|
||||||
>
|
|
||||||
<a-input-password v-bind="field" v-model:value="field.value" />
|
<a-input-password v-bind="field" v-model:value="field.value" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</vee-field>
|
</vee-field>
|
||||||
|
|
||||||
<a-typography-title :level="4" :style="{ textAlign: 'center' }"
|
<a-typography-title :level="4" :style="{ textAlign: 'center' }">Terms</a-typography-title>
|
||||||
>Terms</a-typography-title
|
|
||||||
>
|
|
||||||
<div class="maxHeightScroller">
|
<div class="maxHeightScroller">
|
||||||
<div style="height: 100%">
|
<div style="height: 100%">
|
||||||
<terms-of-services />
|
<terms-of-services />
|
||||||
<vee-field
|
<vee-field name="agree" :unchecked-value="false" type="checkbox" v-slot="{ field, value, errorMessage }">
|
||||||
name="agree"
|
|
||||||
:unchecked-value="false"
|
|
||||||
type="checkbox"
|
|
||||||
v-slot="{ field, value, errorMessage }"
|
|
||||||
>
|
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
@update:checked="
|
@update:checked="
|
||||||
(n) => {
|
(n) => {
|
||||||
@ -179,9 +133,7 @@ notification[ "error" ]({
|
|||||||
<div ref="root" />
|
<div ref="root" />
|
||||||
<a-row :align="'middle'" justify="center">
|
<a-row :align="'middle'" justify="center">
|
||||||
<a-col>
|
<a-col>
|
||||||
<a-button size="large" type="primary" html-type="submit"
|
<a-button size="large" type="primary" html-type="submit">Sign me up!</a-button>
|
||||||
>Sign me up!</a-button
|
|
||||||
>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { IChapter } from "@models/stories/chapter";
|
|
||||||
import { IStory } from "@models/stories";
|
|
||||||
import { storyEditMiddleware } from "@client/middleware";
|
import { storyEditMiddleware } from "@client/middleware";
|
||||||
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
|
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
|
||||||
import SingleChapter from "~/components/story/create/singleChapter.vue";
|
import SingleChapter from "~/components/story/create/singleChapter.vue";
|
||||||
@ -13,9 +11,7 @@
|
|||||||
const rtr = useRoute();
|
const rtr = useRoute();
|
||||||
const {
|
const {
|
||||||
data: { value: originalStory },
|
data: { value: originalStory },
|
||||||
} = await useApiFetch<SingleChapterResult | null>(
|
} = await useApiFetch<SingleChapterResult | null>(`/story/${rtr.params.id}/${rtr.params.cidx}`);
|
||||||
`/story/${rtr.params.id}/${rtr.params.cidx}`,
|
|
||||||
);
|
|
||||||
if (originalStory === null) {
|
if (originalStory === null) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
@ -28,8 +24,5 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<single-chapter
|
<single-chapter :data="toFormChapter(originalStory!.currentChapter)" :name="originalStory!.currentChapter.title" />
|
||||||
:data="toFormChapter(originalStory!.currentChapter)"
|
|
||||||
:name="originalStory!.currentChapter.title"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
import { IChapter } from "@models/stories/chapter";
|
import { IChapter } from "@models/stories/chapter";
|
||||||
|
|
||||||
import { storyEditMiddleware } from "@client/middleware";
|
import { storyEditMiddleware } from "@client/middleware";
|
||||||
|
import { IBand } from "@models/band";
|
||||||
|
import { IUser } from "@models/user";
|
||||||
const rtr = useRoute();
|
const rtr = useRoute();
|
||||||
const {
|
const {
|
||||||
data: { value: originalStory },
|
data: { value: originalStory },
|
||||||
} = await useApiFetch<
|
} = await useApiFetch<({ chapters: (IChapter & { text: string })[] } & IStory) | null>(`/story/${rtr.params.id}/full`);
|
||||||
({ chapters: (IChapter & { text: string })[] } & IStory) | null
|
|
||||||
>(`/story/${rtr.params.id}/full`);
|
|
||||||
if (originalStory === null) {
|
if (originalStory === null) {
|
||||||
console.log("IT DOESN'T EXIST DAWG");
|
console.log("IT DOESN'T EXIST DAWG");
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -20,18 +20,18 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: [storyEditMiddleware, "auth"],
|
middleware: ["auth", storyEditMiddleware],
|
||||||
});
|
});
|
||||||
const story: FormStory = {
|
const story: FormStory = {
|
||||||
title: originalStory!.title,
|
title: originalStory!.title,
|
||||||
coAuthor: originalStory?.coAuthor ? originalStory.coAuthor._id : null,
|
coAuthor: originalStory?.coAuthor ? (originalStory.coAuthor as IUser)._id : null,
|
||||||
completed: originalStory!.completed,
|
completed: originalStory!.completed,
|
||||||
chapters: originalStory!.chapters.map((a, i) => ({
|
chapters: originalStory!.chapters.map((a, i) => ({
|
||||||
...a,
|
...a,
|
||||||
id: a.id,
|
id: a.id,
|
||||||
chapterTitle: a.title,
|
chapterTitle: a.title,
|
||||||
index: i + 1,
|
index: i + 1,
|
||||||
bands: a.bands.map((a) => a._id),
|
bands: (a.bands as IBand[]).map((a) => a._id),
|
||||||
content: a.text,
|
content: a.text,
|
||||||
uuidKey: v4(),
|
uuidKey: v4(),
|
||||||
})),
|
})),
|
||||||
@ -41,14 +41,6 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a-typography-title style="text-align: center">
|
<a-typography-title style="text-align: center"> Editing "{{ originalStory?.title }}" </a-typography-title>
|
||||||
Editing "{{ originalStory?.title }}"
|
<story-form :can-draft="false" :data="story" :endpoint="`/story/${rtr.params.id}`" endpoint-method="put"> </story-form>
|
||||||
</a-typography-title>
|
|
||||||
<story-form
|
|
||||||
:can-draft="false"
|
|
||||||
:data="story"
|
|
||||||
:endpoint="`/story/${rtr.params.id}`"
|
|
||||||
endpoint-method="put"
|
|
||||||
>
|
|
||||||
</story-form>
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { format, formatDistanceToNow, formatRelative } from "date-fns";
|
import { format, formatDistanceToNow } from "date-fns";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
import { IStory } from "@models/stories";
|
|
||||||
import { favourites } from "@client/listActions";
|
import { favourites } from "@client/listActions";
|
||||||
import singleStory from "~/components/listings/singleStory.vue";
|
import singleStory from "~/components/listings/singleStory.vue";
|
||||||
import icon from "~/components/icon.vue";
|
import icon from "~/components/icon.vue";
|
||||||
@ -16,10 +15,7 @@
|
|||||||
}
|
}
|
||||||
const activeKey = ref("main");
|
const activeKey = ref("main");
|
||||||
const activeFavKey = ref("favs/stories");
|
const activeFavKey = ref("favs/stories");
|
||||||
const uLastVisit = Date.parse(
|
const uLastVisit = Date.parse((userInfo.value?.lastVisit || userInfo.value?.lastLogin) as unknown as string);
|
||||||
(userInfo.value?.lastVisit ||
|
|
||||||
userInfo.value?.lastLogin) as unknown as string,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isSelf = userInfo.value?._id === parseInt(rtr.params.id as string);
|
const isSelf = userInfo.value?._id === parseInt(rtr.params.id as string);
|
||||||
useHead({
|
useHead({
|
||||||
@ -32,10 +28,7 @@
|
|||||||
<div style="height: fit-content">
|
<div style="height: fit-content">
|
||||||
<a-card-meta style="align-items: center; margin: 0.5em">
|
<a-card-meta style="align-items: center; margin: 0.5em">
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
<a-avatar
|
<a-avatar :size="75" :src="`/avatars/${userInfo?.profile.avatar}.png`" />
|
||||||
:size="75"
|
|
||||||
:src="`/avatars/${userInfo?.profile.avatar}.png`"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-typography-title :level="1" style="margin-top: 0.5em">
|
<a-typography-title :level="1" style="margin-top: 0.5em">
|
||||||
@ -48,15 +41,9 @@
|
|||||||
<template #text> Administrator </template>
|
<template #text> Administrator </template>
|
||||||
</a-badge-ribbon>
|
</a-badge-ribbon>
|
||||||
</template>
|
</template>
|
||||||
<a-descriptions
|
<a-descriptions :column="2" :labelStyle="{ fontWeight: 'bold' }" :colon="false">
|
||||||
:column="2"
|
|
||||||
:labelStyle="{ fontWeight: 'bold' }"
|
|
||||||
:colon="false"
|
|
||||||
>
|
|
||||||
<a-descriptions-item label="Last visit">
|
<a-descriptions-item label="Last visit">
|
||||||
<div v-if="userInfo?.banned" style="color: red; font-weight: bold">
|
<div v-if="userInfo?.banned" style="color: red; font-weight: bold">BANNED</div>
|
||||||
BANNED
|
|
||||||
</div>
|
|
||||||
<div v-else-if="userInfo?.profile.hidden">
|
<div v-else-if="userInfo?.profile.hidden">
|
||||||
<i>Unknown</i>
|
<i>Unknown</i>
|
||||||
</div>
|
</div>
|
||||||
@ -64,38 +51,23 @@
|
|||||||
<span>
|
<span>
|
||||||
{{ format(uLastVisit, "MM/dd/yyyy @ hh:mm:ss a") }}
|
{{ format(uLastVisit, "MM/dd/yyyy @ hh:mm:ss a") }}
|
||||||
</span>
|
</span>
|
||||||
<span style="margin-left: 0.6em">
|
<span style="margin-left: 0.6em"> ({{ formatDistanceToNow(uLastVisit, { addSuffix: true }) }}) </span>
|
||||||
({{ formatDistanceToNow(uLastVisit, { addSuffix: true }) }})
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="Website">
|
<a-descriptions-item label="Website">
|
||||||
<a
|
<a target="_blank" :href="`${userInfo?.profile.website.startsWith('http') ? '' : 'http://'}${userInfo?.profile.website}`">
|
||||||
target="_blank"
|
|
||||||
:href="`${
|
|
||||||
userInfo?.profile.website.startsWith('http') ? '' : 'http://'
|
|
||||||
}${userInfo?.profile.website}`"
|
|
||||||
>
|
|
||||||
{{ userInfo?.profile.website }}
|
{{ userInfo?.profile.website }}
|
||||||
</a>
|
</a>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="Blog/Journal">
|
<a-descriptions-item label="Blog/Journal">
|
||||||
<a
|
<a target="_blank" :href="`${userInfo?.profile.blog.startsWith('http') ? '' : 'http://'}${userInfo?.profile.blog}`">
|
||||||
target="_blank"
|
|
||||||
:href="`${
|
|
||||||
userInfo?.profile.blog.startsWith('http') ? '' : 'http://'
|
|
||||||
}${userInfo?.profile.blog}`"
|
|
||||||
>
|
|
||||||
{{ userInfo?.profile.blog }}
|
{{ userInfo?.profile.blog }}
|
||||||
</a>
|
</a>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="Occupation">
|
<a-descriptions-item label="Occupation">
|
||||||
{{ userInfo?.profile.occupation }}
|
{{ userInfo?.profile.occupation }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item
|
<a-descriptions-item label="Email" v-if="userInfo?.profile.showEmail || ses?.user?.profile.isAdmin">
|
||||||
label="Email"
|
|
||||||
v-if="userInfo?.profile.showEmail || ses?.user?.profile.isAdmin"
|
|
||||||
>
|
|
||||||
{{ userInfo?.email }}
|
{{ userInfo?.email }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
@ -129,18 +101,7 @@
|
|||||||
<single-story :last="true" :story="item" />
|
<single-story :last="true" :story="item" />
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="1" v-if="isSelf">
|
<a-col :span="1" v-if="isSelf">
|
||||||
<a
|
<a style="color: red" @click="() => favourites(userInfo?.favs.stories || [], item._id, true, 'story')">
|
||||||
style="color: red"
|
|
||||||
@click="
|
|
||||||
() =>
|
|
||||||
favourites(
|
|
||||||
userInfo?.favs.stories || [],
|
|
||||||
item._id,
|
|
||||||
true,
|
|
||||||
'story',
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<icon istyle="regular" name="trash" color="#f00" />
|
<icon istyle="regular" name="trash" color="#f00" />
|
||||||
</a>
|
</a>
|
||||||
</a-col>
|
</a-col>
|
||||||
@ -149,16 +110,10 @@
|
|||||||
</a-list>
|
</a-list>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="favs/authors" tab="Authors">
|
<a-tab-pane key="favs/authors" tab="Authors">
|
||||||
<a-list
|
<a-list item-layout="horizontal" :data-source="userInfo?.favs.authors" :grid="{ gutter: 16, column: 3 }">
|
||||||
item-layout="horizontal"
|
|
||||||
:data-source="userInfo?.favs.authors"
|
|
||||||
:grid="{ gutter: 16, column: 3 }"
|
|
||||||
>
|
|
||||||
<template #renderItem="{ item }">
|
<template #renderItem="{ item }">
|
||||||
<a-list-item style="display: flex; align-items: center">
|
<a-list-item style="display: flex; align-items: center">
|
||||||
<a-list-item-meta
|
<a-list-item-meta style="align-items: center; width: min-content">
|
||||||
style="align-items: center; width: min-content"
|
|
||||||
>
|
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
<a-avatar :src="`/avatars/${item.profile.avatar}.png`" />
|
<a-avatar :src="`/avatars/${item.profile.avatar}.png`" />
|
||||||
</template>
|
</template>
|
||||||
@ -170,18 +125,7 @@
|
|||||||
</a-list-item-meta>
|
</a-list-item-meta>
|
||||||
<template #actions v-if="isSelf">
|
<template #actions v-if="isSelf">
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a style="color: red" @click="() => favourites(userInfo?.favs.authors || [], item._id, true, 'author')">
|
||||||
style="color: red"
|
|
||||||
@click="
|
|
||||||
() =>
|
|
||||||
favourites(
|
|
||||||
userInfo?.favs.authors || [],
|
|
||||||
item._id,
|
|
||||||
true,
|
|
||||||
'author',
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<icon istyle="regular" name="trash" color="#f00" />
|
<icon istyle="regular" name="trash" color="#f00" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import mongoose from "mongoose";
|
|
||||||
import * as net from "net";
|
|
||||||
import plugnplay from "@server/plugnplay";
|
import plugnplay from "@server/plugnplay";
|
||||||
export default defineNuxtPlugin({
|
export default defineNuxtPlugin({
|
||||||
name: "mongo",
|
name: "mongo",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import mongoose from "mongoose";
|
import mongoose from "mongoose";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { IUser, User } from "@models/user";
|
import { User } from "@models/user";
|
||||||
import { log } from "@server/logger";
|
import { log } from "@server/logger";
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
@ -25,8 +25,7 @@ export default eventHandler(async (event) => {
|
|||||||
if (!user.auth.emailVerified) {
|
if (!user.auth.emailVerified) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message:
|
message: 'Account inactive!<br><a href="/activate/resend">Resend verification</a>?',
|
||||||
'Account inactive!<br><a href="/activate/resend">Resend verification</a>?',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let tok = user.generateJWT(useRuntimeConfig().jwt);
|
let tok = user.generateJWT(useRuntimeConfig().jwt);
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { log } from "@server/logger";
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
let ahead = (
|
let ahead = (getHeaders(event).authorization || getCookie(event, "auth:token") || "")?.replace("Bearer ", "");
|
||||||
getHeaders(event).authorization ||
|
|
||||||
getCookie(event, "auth:token") ||
|
|
||||||
""
|
|
||||||
)?.replace("Bearer ", "");
|
|
||||||
if (event.context.currentUser) {
|
if (event.context.currentUser) {
|
||||||
return {
|
return {
|
||||||
token: ahead,
|
token: ahead,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { messages } from "@server/constants";
|
import { messages } from "@server/constants";
|
||||||
import { isAdmin } from "@server/middlewareButNotReally";
|
import { isAdmin } from "@server/middlewareButNotReally";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
|
||||||
import { Band, IBand } from "@models/band";
|
import { Band, IBand } from "@models/band";
|
||||||
|
|
||||||
export default eventHandler(async (ev) => {
|
export default eventHandler(async (ev) => {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { listQuerier } from "@server/dbHelpers";
|
import { listQuerier } from "@server/dbHelpers";
|
||||||
import { Band } from "@models/band";
|
import { Band } from "@models/band";
|
||||||
import { Story } from "@models/stories";
|
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
const params = getRouterParams(event);
|
const params = getRouterParams(event);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { usernameRegex } from "@server/constants";
|
|
||||||
import { User } from "@models/user";
|
import { User } from "@models/user";
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import san from "sanitize-html";
|
import san from "sanitize-html";
|
||||||
import { storyQuerier } from "@server/dbHelpers";
|
import { storyQuerier } from "@server/dbHelpers";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
import { isLoggedIn } from "@server/middlewareButNotReally";
|
||||||
import { Story } from "@models/stories";
|
|
||||||
import { Review } from "@models/stories/review";
|
import { Review } from "@models/stories/review";
|
||||||
|
|
||||||
export default eventHandler(async (ev) => {
|
export default eventHandler(async (ev) => {
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
import { Readable } from "stream";
|
|
||||||
import { Document } from "mongoose";
|
import { Document } from "mongoose";
|
||||||
import { IStory, Story } from "@models/stories";
|
import { IStory, Story } from "@models/stories";
|
||||||
import { FormStory } from "@client/types/form/story";
|
import { FormStory } from "@client/types/form/story";
|
||||||
import { storyQuerier } from "@server/dbHelpers";
|
import { storyQuerier } from "@server/dbHelpers";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
import { isLoggedIn } from "@server/middlewareButNotReally";
|
||||||
import { canModify } from "@server/middlewareButNotReally/storyPrivileges";
|
import { canModify } from "@server/middlewareButNotReally/storyPrivileges";
|
||||||
import {
|
import { bodyHandler, getBucket, modelFormChapter, replaceOrUploadContent } from "@server/storyHelpers";
|
||||||
bodyHandler,
|
|
||||||
getBucket,
|
|
||||||
modelFormChapter,
|
|
||||||
replaceOrUploadContent,
|
|
||||||
} from "@server/storyHelpers";
|
|
||||||
import { countWords } from "@functions";
|
import { countWords } from "@functions";
|
||||||
import { messages } from "@server/constants";
|
import { messages } from "@server/constants";
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import san from "sanitize-html";
|
|
||||||
import { FormStory } from "@client/types/form/story";
|
import { FormStory } from "@client/types/form/story";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
import { isLoggedIn } from "@server/middlewareButNotReally";
|
||||||
import { getBucket, bodyHandler, modelFormChapter } from "@server/storyHelpers";
|
import { getBucket, bodyHandler, modelFormChapter } from "@server/storyHelpers";
|
||||||
import { Story } from "@models/stories";
|
import { Story } from "@models/stories";
|
||||||
import { sanitizeConf } from "@server/constants";
|
|
||||||
import { countWords } from "@functions";
|
import { countWords } from "@functions";
|
||||||
|
|
||||||
export default eventHandler(async (ev) => {
|
export default eventHandler(async (ev) => {
|
||||||
@ -24,9 +22,7 @@ export default eventHandler(async (ev) => {
|
|||||||
});
|
});
|
||||||
for (const c of body.chapters) {
|
for (const c of body.chapters) {
|
||||||
story.chapters.push(modelFormChapter(c));
|
story.chapters.push(modelFormChapter(c));
|
||||||
story.chapters[story.chapters.length - 1].words = countWords(
|
story.chapters[story.chapters.length - 1].words = countWords(await bodyHandler(c));
|
||||||
await bodyHandler(c),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
await story.save();
|
await story.save();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FavPayload, SubPayload } from "@client/types/form/favSub";
|
import { FavPayload } from "@client/types/form/favSub";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
import { isLoggedIn } from "@server/middlewareButNotReally";
|
||||||
import { User } from "@models/user";
|
import { User } from "@models/user";
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import san from "sanitize-html";
|
|
||||||
import { weirdToNormalChars } from "weird-to-normal-chars";
|
import { weirdToNormalChars } from "weird-to-normal-chars";
|
||||||
import { Profile, MyStuff } from "@client/types/form/myStuff";
|
import { MyStuff } from "@client/types/form/myStuff";
|
||||||
import { apiRoot, messages } from "@server/constants";
|
import { apiRoot } from "@server/constants";
|
||||||
import { isLoggedIn } from "@server/middlewareButNotReally";
|
import { isLoggedIn } from "@server/middlewareButNotReally";
|
||||||
import { Review } from "@models/stories/review";
|
import { Review } from "@models/stories/review";
|
||||||
import { IUser, User } from "@models/user";
|
import { IUser, User } from "@models/user";
|
||||||
@ -51,14 +50,11 @@ export default eventHandler(async (ev) => {
|
|||||||
if (exists) {
|
if (exists) {
|
||||||
throw createError(emsg("username"));
|
throw createError(emsg("username"));
|
||||||
}
|
}
|
||||||
let { data: lookup } = await axios.get(
|
let { data: lookup } = await axios.get(`${apiRoot}/session-sharing/lookup`, {
|
||||||
`${apiRoot}/session-sharing/lookup`,
|
params: {
|
||||||
{
|
id: ev.context.currentUser!._id,
|
||||||
params: {
|
|
||||||
id: ev.context.currentUser!._id,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
await axios.put(`${apiRoot}/v3/users/${lookup.value.uid}`, {
|
await axios.put(`${apiRoot}/v3/users/${lookup.value.uid}`, {
|
||||||
body: {
|
body: {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import mongoose from "mongoose";
|
|
||||||
import { log } from "@server/logger";
|
import { log } from "@server/logger";
|
||||||
import plugnplay from "@server/plugnplay";
|
import plugnplay from "@server/plugnplay";
|
||||||
|
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { log } from "@server/logger";
|
|
||||||
import { User } from "@models/user";
|
import { User } from "@models/user";
|
||||||
|
import { messages } from "@server/constants";
|
||||||
|
import { AccessToken } from "@models/oauth";
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
let ahead = (
|
let ahead = (getHeaders(event).authorization || getCookie(event, "auth:token") || "")?.replace("Bearer ", "");
|
||||||
getHeaders(event).authorization ||
|
|
||||||
getCookie(event, "auth:token") ||
|
|
||||||
""
|
|
||||||
)?.replace("Bearer ", "");
|
|
||||||
// console.log("in here fucknuts", ahead);
|
|
||||||
// log.debug(`'${ahead}'`, { label: "idk" });
|
|
||||||
if (ahead) {
|
if (ahead) {
|
||||||
let toktok = jwt.verify(
|
let toktok: jwt.JwtPayload;
|
||||||
ahead,
|
try {
|
||||||
// ahead.replace("Bearer ", ""),
|
toktok = jwt.verify(ahead, useRuntimeConfig().jwt) as jwt.JwtPayload;
|
||||||
useRuntimeConfig().jwt,
|
let user = await User.findById(toktok.id as number).exec();
|
||||||
) as jwt.JwtPayload;
|
if (user && toktok) event.context.currentUser = user;
|
||||||
let user = await User.findById(toktok.id as number).exec();
|
} catch (e) {
|
||||||
if (user && toktok) event.context.currentUser = user;
|
const t = await AccessToken.findOne({ token: ahead });
|
||||||
// setCookie(event, "auth:token", ahead)
|
if (!t)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: messages[401],
|
||||||
|
});
|
||||||
|
let user = await User.findById(t.userID);
|
||||||
|
if (user) event.context.currentUser = user;
|
||||||
|
// else throw createError({statusCode: 401, message: messages[401]})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
1
typings/express.d.ts
vendored
1
typings/express.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
import { Document } from "mongoose";
|
|
||||||
import { IStory } from "@models/stories";
|
import { IStory } from "@models/stories";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
|
1
typings/h3.d.ts
vendored
1
typings/h3.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
import type { H3Event, H3EventContext } from "h3";
|
|
||||||
import { IFicmas } from "@models/challenges/ficmas";
|
import { IFicmas } from "@models/challenges/ficmas";
|
||||||
import { IUser } from "@models/user";
|
import { IUser } from "@models/user";
|
||||||
declare module "h3" {
|
declare module "h3" {
|
||||||
|
Loading…
Reference in New Issue
Block a user