style(*): cleanup unused imports

This commit is contained in:
parent 5baa00bbb4
commit c8e84c909e
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
53 changed files with 195 additions and 748 deletions

@ -3,6 +3,5 @@
"useTabs": true,
"trailingComma": "all",
"arrowParens": "always",
"vueIndentScriptAndStyle": true,
"editorconfig": true
"vueIndentScriptAndStyle": true
}

@ -1,14 +1,11 @@
<script lang="ts" setup>
import { theme } from "ant-design-vue";
const { getSession, signIn } = useAuth();
await getSession({ force: true });
const { data } = await useAuth();
const dop = data?.value as any;
let darkBool = ref(dop?.user?.profile?.nightMode || false);
let darkBool = ref(data.value?.user?.profile?.nightMode || false);
// provide("user", ref(dop?.user || null));
provide("dark", darkBool);
useHead({
@ -17,8 +14,7 @@
return darkBool.value ? "dark" : undefined;
}).value,
},
titleTemplate: (title) =>
title ? `Rockfic | ${title}` : "Rockfic | Band fiction that rocks",
titleTemplate: (title) => (title ? `Rockfic | ${title}` : "Rockfic | Band fiction that rocks"),
});
// provide("loaded", useNuxtApp().$loaded);
// let loaded = ref<boolean[]>([]);

@ -1,9 +1,7 @@
<script setup lang="ts">
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 tinymce from "tinymce";
const props = defineProps<{
name: string;
init: any;
@ -16,17 +14,8 @@
</script>
<template>
<ClientOnly>
<vee-field
:name="props.name"
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"
>
<vee-field :name="props.name" 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
v-bind="field"
width="100%"

@ -1,7 +1,4 @@
<script lang="ts" setup>
import { log } from "@server/logger";
import { MenuProps } from "ant-design-vue";
const { data, status } = useAuth();
const itemMap = ref({
home: "/",
@ -18,11 +15,7 @@
admin: "/admin",
logout: "/auth/logout",
});
let cur = ref<string>(
Object.keys(itemMap.value).find(
(a) => itemMap.value[a] === useRoute().path,
) || useRoute().path,
);
let cur = ref<string>(Object.keys(itemMap.value).find((a) => itemMap.value[a] === useRoute().path) || useRoute().path);
let selected: string[] = [cur.value];
const clickFn = (minfo) => {
@ -61,18 +54,11 @@
<a-menu-item key="reviews"> Manage Reviews </a-menu-item>
<a-menu-item key="messages"> Private Messages </a-menu-item>
</a-sub-menu>
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false">
Admin
</a-menu-item>
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin </a-menu-item>
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
</a-menu>
<div>
<a-button
v-if="data?.user"
type="primary"
tooltip="Post a New Story"
@click="() => navigateTo('/new-story')"
>
<a-button v-if="data?.user" type="primary" tooltip="Post a New Story" @click="() => navigateTo('/new-story')">
<!-- <template #icon>
</template> -->
<icon istyle="regular" name="file-plus" />
@ -80,16 +66,8 @@
</a-button>
</div>
<div class="acbut" v-if="!data">
<a-button size="large" @click="() => navigateTo('/auth/login')">
Login
</a-button>
<a-button
size="large"
type="primary"
@click="() => navigateTo('/auth/register')"
>
Register
</a-button>
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login </a-button>
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register </a-button>
</div>
</div>
</template>

@ -1,12 +1,9 @@
<script setup lang="ts">
import type {
MenuItemType,
SubMenuType,
} from "ant-design-vue/es/menu/src/interface";
import type { MenuItemType, SubMenuType } from "ant-design-vue/es/menu/src/interface";
import { ItemType, theme } from "ant-design-vue";
import Icon from "../icon.vue";
import { ISidebarItem } from "@models/sidebarEntry";
import { AButton, NuxtLink } from "#components";
import { NuxtLink } from "#components";
const loaded = inject<Ref<boolean>>("loaded");

@ -1,6 +1,5 @@
<script lang="ts" setup>
import type { IStory } from "@models/stories";
import { log } from "~/lib/server/logger";
import { format } from "date-fns";
import icon from "../icon.vue";
import { theme } from "ant-design-vue";
@ -14,18 +13,8 @@
const idxo = (prop.last || false ? prop.story.chapters.length : 1) - 1;
// console.log("idx0->", idxo)
// log.debug("posti->", prop.story.chapters[ prop.story.chapters.length - 1 ]);
const shortDate = format(
Date.parse(
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",
);
const shortDate = format(Date.parse(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>
<template>
<a-card>
@ -36,26 +25,16 @@
{{ story.title }}
</NuxtLink>
<a-tooltip placement="topLeft">
<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>
<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>
<icon v-if="!data?.user" istyle="solid" name="lock" />
</a-tooltip>
</div>
<div
style="display: flex; font-size: 0.9em; align-items: baseline"
class="headerthing"
>
<div style="display: flex; font-size: 0.9em; align-items: baseline" class="headerthing">
<span> a </span>
<div
style=""
v-for="(band, idx) in story.chapters[idxo].bands.filter((a) => !!a)"
>
<div style="" v-for="(band, idx) in story.chapters[idxo].bands.filter((a) => !!a)">
<span>
<NuxtLink :to="`/band/${band._id}`"> {{ band.name }} </NuxtLink
>{{ idx < story.chapters[idxo].bands.length - 1 ? ", " : "" }}
<NuxtLink :to="`/band/${band._id}`"> {{ band.name }} </NuxtLink>{{ idx < story.chapters[idxo].bands.length - 1 ? ", " : "" }}
</span>
</div>
<span> fic by </span>
@ -100,9 +79,7 @@
<div class="storyMeta">
<div class="inner">
<span v-if="story.chapters.length > 1">
<NuxtLink :to="`/story/${story._id}/chapters`">
{{ story.chapters.length }} chapters
</NuxtLink>
<NuxtLink :to="`/story/${story._id}/chapters`"> {{ story.chapters.length }} chapters </NuxtLink>
</span>
<span v-else> {{ story.chapters.length }} chapter </span>
<span>
@ -121,15 +98,10 @@
{{ story.chapters[idxo].characters.join(", ") }}
</meta-item>
<meta-item label="Relationship(s)">
<div
style="display: inline-block"
v-for="(rel, idx) in story.chapters[idxo].relationships"
>
<div style="display: inline-block" v-for="(rel, idx) in story.chapters[idxo].relationships">
<span>
{{ Array.isArray(rel) ? rel.join("/") : rel }}
{{
idx < story.chapters[idxo].relationships.length - 1 ? "," : ""
}}
{{ idx < story.chapters[idxo].relationships.length - 1 ? "," : "" }}
</span>
</div>
<span v-if="story.chapters[idxo].relationships.length < 1">
@ -149,12 +121,7 @@
<div class="stats">
<span>
<span class="staticon">
<icon
:istyle="!dark ? 'solid' : 'regular'"
icolor="#ff2883"
:size="12"
name="heart"
/>
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#ff2883" :size="12" name="heart" />
</span>
<span>
{{ story.favs }}
@ -162,12 +129,7 @@
</span>
<span>
<span class="staticon">
<icon
:istyle="!dark ? 'solid' : 'regular'"
icolor="#1787d7"
:size="12"
name="book-open"
/>
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#1787d7" :size="12" name="book-open" />
</span>
<span>
{{ story.views }}
@ -175,12 +137,7 @@
</span>
<span>
<span class="staticon">
<icon
:istyle="!dark ? 'solid' : 'regular'"
icolor="#51e07c"
:size="12"
name="thumbs-up"
/>
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#51e07c" :size="12" name="thumbs-up" />
</span>
<span>
{{ story.recs }}
@ -188,12 +145,7 @@
</span>
<span>
<span class="staticon">
<icon
:istyle="!dark ? 'solid' : 'regular'"
icolor="#c2d420"
:size="12"
name="download"
/>
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#c2d420" :size="12" name="download" />
</span>
<span>
{{ story.downloads }}

@ -1,5 +1,5 @@
<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 { comment } from "@client/editorConfig";
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
@ -25,13 +25,10 @@
};
const replySubmit = async (values) => {
const { data } = await useApiFetch<any>(
`/review/${props.review._id}/reply`,
{
const { data } = await useApiFetch<any>(`/review/${props.review._id}/reply`, {
method: "post",
body: values,
},
);
});
review.value.replies.push(data.value.data);
replyFormVisible.value = false;
};
@ -49,12 +46,7 @@
<a-comment>
<template #actions>
<div v-if="!!data?.user" class="review-actions">
<a-button
v-if="isCommentAuthor"
@click="() => (isEditing = !isEditing)"
>
Edit
</a-button>
<a-button v-if="isCommentAuthor" @click="() => (isEditing = !isEditing)"> Edit </a-button>
<a-popconfirm
title="Are you sure you want to permanently delete this review?"
ok-text="Yes"
@ -64,13 +56,7 @@
>
<a-button> Delete </a-button>
</a-popconfirm>
<a-button
v-if="isAuthor"
type="primary"
@click="() => (replyFormVisible = !replyFormVisible)"
>
Reply
</a-button>
<a-button v-if="isAuthor" type="primary" @click="() => (replyFormVisible = !replyFormVisible)"> Reply </a-button>
</div>
</template>
<template #author>
@ -91,25 +77,14 @@
<div>
<vee-form @submit="editSubmit" v-if="isEditing">
<vee-field name="content" v-slot="{ value, field, errorMessage }">
<base-editor
:val="review.text"
:width="150"
label=""
:name="field.name"
:init="comment"
/>
<base-editor :val="review.text" :width="150" label="" :name="field.name" :init="comment" />
</vee-field>
<a-button type="primary" html-type="submit"> Edit review </a-button>
</vee-form>
<div v-else v-html="review.text" />
<vee-form @submit="replySubmit" v-if="replyFormVisible">
<vee-field name="content" v-slot="{ value, field, errorMessage }">
<base-editor
:width="150"
label=""
:name="field.name"
:init="comment"
/>
<base-editor :width="150" label="" :name="field.name" :init="comment" />
</vee-field>
<a-button type="primary" html-type="submit"> Post response </a-button>
</vee-form>

@ -3,9 +3,6 @@
import { RuleExpression, useField } from "vee-validate";
import { cs } from "@client/storyFormSchema";
import { IBand } from "@models/band";
import { log } from "@server/logger";
import iconEl from "../icon.vue";
const bandlist = inject<IBand[]>("bandlist");
const fname = inject<string>("curName");
@ -19,20 +16,13 @@
label: a.name,
disabled: a.locked || false,
}));
let bandField = useField(
fname + "bands",
cs.fields.bands as unknown as MaybeRef<RuleExpression<number[]>>,
);
let bandField = useField(fname + "bands", cs.fields.bands as unknown as MaybeRef<RuleExpression<number[]>>);
const { value, errorMessage, name, setValue } = bandField;
// setValue(sb)
</script>
<template>
<a-form-item
:validate-status="!!errorMessage ? 'error' : undefined"
:help="errorMessage"
label="Bands"
>
<a-form-item :validate-status="!!errorMessage ? 'error' : undefined" :help="errorMessage as any" label="Bands">
<a-select
:allow-clear="true"
mode="multiple"

@ -1,15 +1,9 @@
<script setup lang="ts">
import {
Form as VeeForm,
Field,
useForm,
useField,
ErrorMessage,
} from "vee-validate";
import { Field, ErrorMessage } from "vee-validate";
import { NamePath } from "ant-design-vue/es/form/interface";
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 genre from "../atoms/genre.vue";
import elCharacters from "../atoms/characters.vue";
@ -41,16 +35,8 @@
<div>
<a-row :gutter="[10, 0]">
<a-col :span="12">
<Field
:name="name + '.chapterTitle'"
v-slot="{ value, field, errorMessage }"
>
<a-form-item
:name="[field.name]"
label="Chapter title"
:help="errorMessage"
:status="!!errorMessage ? 'error' : undefined"
>
<Field :name="name + '.chapterTitle'" v-slot="{ value, field, errorMessage }">
<a-form-item :name="[field.name as string]" label="Chapter title" :help="errorMessage" :status="!!errorMessage ? 'error' : undefined">
<a-input v-bind="field" />
</a-form-item>
</Field>
@ -61,22 +47,10 @@
</a-row>
<a-row :gutter="[10, 0]">
<a-col :span="12">
<base-editor
v-model:val="acData.summary"
:name="name + '.summary'"
:wrap-col="wrapc"
label="Summary"
:init="bare"
/>
<base-editor v-model:val="acData.summary" :name="name + '.summary'" :wrap-col="wrapc" label="Summary" :init="bare" />
</a-col>
<a-col :span="12">
<base-editor
v-model:val="acData.notes"
:name="name + '.notes'"
:wrap-col="wrapc"
label="Author's notes"
:init="bare"
/>
<base-editor v-model:val="acData.notes" :name="name + '.notes'" :wrap-col="wrapc" label="Author's notes" :init="bare" />
</a-col>
</a-row>
<a-row :gutter="[10, 0]">
@ -87,39 +61,17 @@
<el-pairings />
</a-col>
</a-row>
<Field
:name="name + '.nsfw'"
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>
<Field :name="name + '.nsfw'" 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" />
</Field>
<Field
:name="name + '.loggedInOnly'"
type="checkbox"
:unchecked-value="false"
:value="true"
v-slot="{ value, field, errorMessage }"
>
<Field :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>
<error-message :name="field.name" />
</Field>
<Field
:name="name + '.hidden'"
type="checkbox"
:unchecked-value="false"
:value="true"
v-slot="{ value, field, errorMessage }"
>
<Field :name="name + '.hidden'" type="checkbox" :unchecked-value="false" :value="true" v-slot="{ value, field, errorMessage }">
<a-tooltip>
<template #title>
Hides your story from everyone except you and site admins.
</template>
<template #title> Hides your story from everyone except you and site admins. </template>
<a-checkbox v-bind="field"> Hidden </a-checkbox>
</a-tooltip>
</Field>

@ -2,25 +2,10 @@
import draggable from "vuedraggable";
import { v4 } from "uuid";
import lmove from "lodash-move";
import {
Field,
Form as veeForm,
FieldArray,
FieldEntry,
useForm,
} from "vee-validate";
import { Field, FieldArray, useForm } from "vee-validate";
import { storySchema } from "@client/storyFormSchema";
import {
FormChapter,
FormStory,
defaultChapter,
} from "@client/types/form/story";
import {
autoEdit,
autoSave,
debouncedAutoEdit,
debouncedAutoSave,
} from "@client/utils";
import { FormStory, defaultChapter } from "@client/types/form/story";
import { autoEdit, autoSave, debouncedAutoEdit, debouncedAutoSave } from "@client/utils";
import findUser from "~/components/findUser.vue";
@ -79,35 +64,17 @@
@submit="onSubmit"
@invalid-submit="inval"
> -->
<form
@submit="subCb"
@change="
() =>
canDraft
? debouncedAutoSave(values)
: debouncedAutoEdit(values, endpoint, endpointMethod)
"
>
<form @submit="subCb" @change="() => (canDraft ? debouncedAutoSave(values) : debouncedAutoEdit(values, endpoint, endpointMethod))">
<!-- <a-form v-bind:model="acData"> -->
<Field name="title" v-slot="{ value, field, errorMessage }">
<a-form-item
label="Title"
:help="errorMessage"
:validate-status="!!errorMessage ? 'error' : ''"
>
<a-form-item label="Title" :help="errorMessage" :validate-status="!!errorMessage ? 'error' : ''">
<a-input v-bind="field" :value="value" />
</a-form-item>
</Field>
<a-form-item label="Co-author (optional)">
<find-user :initial-option="null" fieldName="coAuthor" :multi="false" />
</a-form-item>
<Field
:unchecked-value="false"
:value="true"
type="checkbox"
name="completed"
v-slot="{ value, field, errorMessage }"
>
<Field :unchecked-value="false" :value="true" type="checkbox" name="completed" v-slot="{ value, field, errorMessage }">
<a-checkbox v-bind="field"> Complete </a-checkbox>
</Field>
<a-divider />
@ -125,11 +92,7 @@
if (e.moved) {
// log.debug(e.moved);
move(e.moved.oldIndex, e.moved.newIndex);
acData.chapters = lmove(
acData.chapters,
e.moved.oldIndex,
e.moved.newIndex,
);
acData.chapters = lmove(acData.chapters, e.moved.oldIndex, e.moved.newIndex);
// log.debug(toRaw(acData.chapters.map((a) => toRaw(a))));
}
}
@ -194,16 +157,8 @@
</template>
</draggable>
</field-array>
<a-button type="primary" html-type="submit">{{
submitText || "Post"
}}</a-button>
<a-button
html-type="submit"
v-if="canDraft"
@click="() => (otherBtnInvoked = true)"
>
Save for Later
</a-button>
<a-button type="primary" html-type="submit">{{ submitText || "Post" }}</a-button>
<a-button html-type="submit" v-if="canDraft" @click="() => (otherBtnInvoked = true)"> Save for Later </a-button>
</form>
<!-- </vee-form> -->
</template>

@ -1,11 +1,5 @@
<script lang="ts" setup>
import {
Form as VeeForm,
Field as veeField,
useForm,
useField,
ErrorMessage,
} from "vee-validate";
import { useField } from "vee-validate";
import { story } from "@client/editorConfig";
import icon from "~/components/icon.vue";
import baseEditor from "../../baseEditor.vue";
@ -22,12 +16,7 @@
</a-radio-group>
<br />
<br />
<base-editor
label=""
v-if="pvalue === 'pasteOrType'"
:init="story"
:name="fname + 'content'"
/>
<base-editor label="" v-if="pvalue === 'pasteOrType'" :init="story" :name="fname + 'content'" />
<a-upload
v-model:file-list="fileList"
v-else-if="pvalue === 'upload'"
@ -53,10 +42,6 @@
}
"
>
<a-button type="primary">
<icon istyle="regular" name="upload" /><span style="margin-left: 0.5em">
Upload a file</span
>
</a-button>
<a-button type="primary"> <icon istyle="regular" name="upload" /><span style="margin-left: 0.5em"> Upload a file</span> </a-button>
</a-upload>
</template>

@ -1,5 +1,4 @@
import { AsyncData, UseFetchOptions } from "#app";
import { NitroFetchRequest } from "nitropack";
import { AsyncData } from "#app";
const useApiFetch = async <T>(url: string, options?: any) => {
// const { token } = useAuth();

@ -4,7 +4,7 @@
import cfooter from "~/components/layouts/footer.vue";
import sidebarThing from "~/components/layouts/sidebar.vue";
import icon from "~/components/icon.vue";
import { ISidebarItem } from "@models/sidebarEntry";
const { useToken } = theme;
const { token } = useToken();
@ -52,10 +52,7 @@
<div class="stat-block">
<div>
<a-typography-text> Band fiction that rocks </a-typography-text>
<a-typography-text type="secondary">
With {{ totals?.stories ?? 0 }} stories by
{{ totals?.authors ?? 0 }} authors
</a-typography-text>
<a-typography-text type="secondary"> With {{ totals?.stories ?? 0 }} stories by {{ totals?.authors ?? 0 }} authors </a-typography-text>
</div>
</div>
<navbar />

@ -1,39 +1,20 @@
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 emptyChapterTitle = "Chapter title cannot be blank.";
const blankTitle = "Title cannot be blank.";
export const cs = yup.object<FormChapter>().shape({
chapterTitle: yup
.string()
.ensure()
.min(1, emptyChapterTitle)
.trim(emptyChapterTitle)
.required(emptyChapterTitle),
chapterTitle: yup.string().ensure().min(1, emptyChapterTitle).trim(emptyChapterTitle).required(emptyChapterTitle),
summary: yup.string().ensure().min(10, emptySummary).required(emptySummary),
notes: yup.string().ensure(),
bands: yup
.array()
.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"),
bands: yup.array().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
.array()
.ensure()
.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!"),
),
.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!")),
nsfw: yup.boolean().oneOf([true, false]),
loggedInOnly: yup.boolean().when("nsfw", ([nsfw], schema) => {
return nsfw
@ -44,20 +25,13 @@ export const cs = yup.object<FormChapter>().shape({
: schema.oneOf([true, false]);
}),
hidden: yup.boolean().oneOf([true, false]),
pot: yup
.string()
.oneOf(["pasteOrType", "upload"])
.required("Story content is required!"),
pot: yup.string().oneOf(["pasteOrType", "upload"]).required("Story content is required!"),
storytext: yup.string().when("pot", ([pot], schema) => {
return pot === "pasteOrType"
? schema
.test(
"numWords",
"Story must be at least 50 words",
(value: any, context) => {
.test("numWords", "Story must be at least 50 words", (value: any, context) => {
return value?.split(/\W+/).length > 50 || false;
},
)
})
.required("Story text can't be blank!")
: schema.min(0);
}),
@ -98,10 +72,6 @@ export const cs = yup.object<FormChapter>().shape({
export const storySchema = yup.object().shape({
title: yup.string().ensure().min(5, blankTitle).required(blankTitle),
chapters: yup
.array()
.min(1, "There must be at least one chapter.")
.of(cs)
.ensure(),
chapters: yup.array().min(1, "There must be at least one chapter.").of(cs).ensure(),
completed: yup.boolean().oneOf([true, false]),
});

@ -1,5 +1,3 @@
import { readFileSync } from "fs";
import { resolve } from "path";
// import chardet from "chardet";
// import iconv from "iconv-lite";
import { GridFSBucketReadStream } from "mongodb";
@ -15,20 +13,13 @@ export function countWords(string: string) {
return stripHtml(string).result.split(/W+/).length;
}
export function populate<T>(
field: string,
model: string,
): PreMiddlewareFunction<Query<T, T>> {
export function populate<T>(field: string, model: string): PreMiddlewareFunction<Query<T, T>> {
return function (next: () => any) {
this.populate(field, undefined, model);
next();
};
}
export function populateSelected<T>(
field: string,
model: string,
selection: string,
): PreMiddlewareFunction<Query<T, T>> {
export function populateSelected<T>(field: string, model: string, selection: string): PreMiddlewareFunction<Query<T, T>> {
return function (next: () => any) {
this.populate(field, selection, model);
next();
@ -40,15 +31,11 @@ export function isFicmasHidden(story: IStory): boolean {
((story.ficmas as IFicmas)?.year == new Date().getFullYear() &&
(story.ficmas as IFicmas)?.anniversary &&
new Date() < new Date(Date.parse("Aug 1 " + new Date().getFullYear()))) ||
((story.ficmas as IFicmas)?.year == new Date().getFullYear() &&
!(story.ficmas as IFicmas)?.anniversary &&
ficsHidden(Date.now()))
((story.ficmas as IFicmas)?.year == new Date().getFullYear() && !(story.ficmas as IFicmas)?.anniversary && ficsHidden(Date.now()))
);
}
export function stringifyStream(
stream: GridFSBucketReadStream,
): Promise<string> {
export function stringifyStream(stream: GridFSBucketReadStream): Promise<string> {
let chunks: Buffer[] = [];
return new Promise((res, rej) => {
stream.on("data", (c) => chunks.push(Buffer.from(c)));

@ -1,6 +1,5 @@
import { H3Event, EventHandlerRequest } from "h3";
import { GridFSBucket } from "mongodb";
import mongoose, { Document } from "mongoose";
import { Document } from "mongoose";
import { norm, stringifyStream } from "@functions";
import { IStory } from "@models/stories";
import { IChapter } from "@models/stories/chapter";
@ -16,11 +15,7 @@ export default async function (
const cloned: any & { chapters: IChapter[] } = { ...finObj };
delete finObj.chapters;
const bucket = getBucket();
let ds = bucket.openDownloadStreamByName(
`/stories/${
cloned.chapters[cindex || event.context.chapterIndex || 0].id
}.txt`,
);
let ds = bucket.openDownloadStreamByName(`/stories/${cloned.chapters[cindex || event.context.chapterIndex || 0].id}.txt`);
let stream = await stringifyStream(ds);
finObj.currentChapter = {
...cloned.chapters[cindex || event.context.chapterIndex || 0],

@ -1,4 +1,3 @@
import { Band } from "@models/band";
import { Challenge } from "@models/challenges/gen";
import { IStory, Story } from "@models/stories";
import { log } from "../logger";

@ -1,16 +1,12 @@
import { H3Event, EventHandlerRequest } from "h3";
import { apiRoot } from "./constants";
export default async function (id: number): Promise<number> {
let { data: lookup } = await useFetch<any>(
`${apiRoot}/session-sharing/lookup`,
{
let { data: lookup } = await useFetch<any>(`${apiRoot}/session-sharing/lookup`, {
method: "get",
query: {
params: {
id,
},
},
},
);
});
return lookup.value.uid as number;
}

@ -1,12 +1,10 @@
import winston from "winston";
const { combine, timestamp, json, splat, printf, colorize } = winston.format;
winston.add;
// winston.add;
const fmt = printf(({ timestamp, level, message, label, durationMs }) => {
return `${timestamp} [${label || "misc"}] ${message} ${
!!durationMs ? " (took " + durationMs + "ms)" : ""
}`;
return `${timestamp} [${label || "misc"}] ${message} ${!!durationMs ? " (took " + durationMs + "ms)" : ""}`;
});
const cfmt = combine(json(), timestamp(), fmt);

@ -2,7 +2,6 @@ import { EventHandlerRequest, H3Event } from "h3";
import { messages } from "@server/constants";
import { IStory } from "@models/stories";
import { isFicmasHidden } from "@functions";
import { IDraft } from "@models/stories/draft";
import axios from "axios";
import { IUser } from "@models/user";
export function isIdNan(ev: H3Event<EventHandlerRequest>) {
@ -33,11 +32,7 @@ export function isLoggedIn(ev: H3Event<EventHandlerRequest>) {
}
}
export async function storyCheck(
event: H3Event<EventHandlerRequest>,
story: IStory,
idx: number,
) {
export async function storyCheck(event: H3Event<EventHandlerRequest>, story: IStory, idx: number) {
let ret: any = {};
if (!story) {
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.`,
};
}
} else if (
story.chapters[idx]?.hidden &&
event.context.currentUser?._id !== (story.author as IUser)._id &&
!event.context.currentUser?.profile.isAdmin
) {
} else if (story.chapters[idx]?.hidden && event.context.currentUser?._id !== (story.author as IUser)._id && !event.context.currentUser?.profile.isAdmin) {
ret.statusCode = 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;
import SequenceFactory from "mongoose-sequence";
import { hasMigrated } from "@dbconfig";
@ -30,11 +30,5 @@ const BandSchema = new mongoose.Schema<IBand>({
],
});
hasMigrated &&
!mongoose.models.Band &&
BandSchema.plugin(AutoIncrement, { id: "band" });
export const Band: Model<IBand> = /* mongoose.models.Band || */ model<IBand>(
"Band",
BandSchema,
"bands",
);
hasMigrated && !mongoose.models.Band && 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 SequenceFactory from "mongoose-sequence";
import { hasMigrated } from "@dbconfig";
@ -67,8 +67,5 @@ const biffnoschema = new mongoose.Schema<IBiffno>({
},
});
hasMigrated &&
!mongoose.models.Biffno &&
biffnoschema.plugin(AutoIncrement, { start_seq: 1, id: "bif_id" });
export const Biffno: Model<IBiffno> =
mongoose.models.Biffno || mongoose.model("Biffno", biffnoschema, "biffno");
hasMigrated && !mongoose.models.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, {
Schema,
PopulatedDoc,
Document,
Model,
model,
} from "mongoose";
import mongoose, { PopulatedDoc, Model, model } from "mongoose";
import { IBand } from "@models/band";
import { IUser } from "@models/user";
@ -48,9 +42,6 @@ export const FicmasSchema = new mongoose.Schema<IFicmas>({
},
});
hasMigrated &&
!mongoose.models.Ficmas &&
FicmasSchema.plugin(AutoIncrement, { id: "ficmas_wishes", inc_field: "_id" });
hasMigrated && !mongoose.models.Ficmas && FicmasSchema.plugin(AutoIncrement, { id: "ficmas_wishes", inc_field: "_id" });
export const Ficmas: Model<IFicmas> =
mongoose.models.Ficmas || model("Ficmas", FicmasSchema, "ficmas_wishes");
export const Ficmas: Model<IFicmas> = 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 { hasMigrated } from "@dbconfig";
@ -45,9 +45,7 @@ const challengeSchema = new mongoose.Schema<IChallenge>({
},
});
hasMigrated &&
!mongoose.models.Challenge &&
challengeSchema.plugin(AutoIncrement, { id: "challenges" });
hasMigrated && !mongoose.models.Challenge && challengeSchema.plugin(AutoIncrement, { id: "challenges" });
export const Challenge: Model<IChallenge> =
// mongoose.models.Challenge ||
mongoose.model("Challenge", challengeSchema, "challenges");

@ -1,11 +1,4 @@
import mongoose, {
Schema,
connect,
PopulatedDoc,
Document,
Model,
} from "mongoose";
import SequenceFactory from "mongoose-sequence";
import mongoose, { Schema, PopulatedDoc, Model } from "mongoose";
import { IPrivMsg } from "./privMsg";
import { IUser } from "./user";
@ -41,8 +34,4 @@ const InboxSchema = new Schema<IInbox>({
],
});
export const Inbox: Model<IInbox> = mongoose.model<IInbox>(
"Inbox",
InboxSchema,
"inboxes",
);
export const Inbox: Model<IInbox> = mongoose.model<IInbox>("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 { hasMigrated } from "@dbconfig";
import { IUser } from "./user";
@ -50,13 +50,6 @@ const PMSchema = new mongoose.Schema<IPrivMsg>({
},
});
hasMigrated &&
!mongoose.models.PrivMsg &&
PMSchema.plugin(AutoIncrement, { id: "private_message" });
hasMigrated && !mongoose.models.PrivMsg && PMSchema.plugin(AutoIncrement, { id: "private_message" });
export const PrivMsg: Model<IPrivMsg> =
/* mongoose.models.PrivMsg || */ mongoose.model(
"PrivMsg",
PMSchema,
"private_messages",
);
export const PrivMsg: Model<IPrivMsg> = /* 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;
interface IAbstractQM {

@ -1,4 +1,4 @@
import mongoose, { Schema, PopulatedDoc, Document, Model } from "mongoose";
import mongoose, { Model } from "mongoose";
export enum Color {
"orange" = "orange",
@ -35,9 +35,4 @@ const SISchema = new mongoose.Schema<ISidebarItem>({
});
export const SidebarItem: Model<ISidebarItem> =
mongoose.models.SidebarItem ||
/* mongoose.models.SidebarItem || */ mongoose.model(
"SidebarItem",
SISchema,
"sidebar",
);
mongoose.models.SidebarItem || /* 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";
export interface IChapter {
title: string;

@ -1,19 +1,12 @@
import { IStory } from ".";
import { hasMigrated } from "@dbconfig";
import { IBand } from "@models/band";
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 mongoose, { Schema, Model } from "mongoose";
import SequenceFactory from "mongoose-sequence";
import { Chapter } from "./chapter";
const AutoIncrement = SequenceFactory(mongoose);
export type IDraft = Omit<
IStory,
"recs" | "favs" | "reviews" | "views" | "downloads" | "posted"
>;
export type IDraft = Omit<IStory, "recs" | "favs" | "reviews" | "views" | "downloads" | "posted">;
// const Cha
@ -39,9 +32,6 @@ const DraftSchema = new Schema<IDraft>(
{ timestamps: true },
);
hasMigrated &&
!mongoose.models.Draft &&
DraftSchema.plugin(AutoIncrement, { id: "drafts" });
hasMigrated && !mongoose.models.Draft && DraftSchema.plugin(AutoIncrement, { id: "drafts" });
export const Draft: Model<IDraft> =
/* mongoose.models.Draft || */ mongoose.model("Draft", DraftSchema, "drafts");
export const Draft: Model<IDraft> = /* 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";
const AutoIncrement = SequenceFactory(mongoose);
@ -87,13 +87,6 @@ const StorySchema = new mongoose.Schema<IStory>({
default: new Date(),
},
});
hasMigrated &&
!mongoose.models.Story &&
Chapter.plugin(AutoIncrement, { id: "chapterid", inc_field: "id" });
hasMigrated && !mongoose.models.Story && Chapter.plugin(AutoIncrement, { id: "chapterid", inc_field: "id" });
hasMigrated && StorySchema.plugin(AutoIncrement, { id: "storyid" });
export const Story: Model<IStory> =
/* mongoose.models.Story || */ mongoose.model(
"Story",
StorySchema,
"stories",
);
export const Story: Model<IStory> = /* 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 { hasMigrated } from "@dbconfig";
import { populate, populateSelected } from "@functions";
@ -63,22 +63,9 @@ CommentSchema
// .pre("find", populateSelected("replyingTo", modelName, "-replies -author"))
.pre("findOne", populate("replies", modelName))
.pre("find", populate("replies", modelName))
.pre(
"findOne",
populateSelected("author", "User", "profile username _id blocked"),
)
.pre(
"find",
populateSelected("author", "User", "profile username _id blocked"),
);
.pre("findOne", populateSelected("author", "User", "profile username _id blocked"))
.pre("find", populateSelected("author", "User", "profile username _id blocked"));
hasMigrated &&
!mongoose.models.Review &&
CommentSchema.plugin(AutoIncrement, { id: "reviews" });
hasMigrated && !mongoose.models.Review && CommentSchema.plugin(AutoIncrement, { id: "reviews" });
export const Review: Model<IReview> =
/* mongoose.models.Review || */ mongoose.model(
modelName,
CommentSchema,
"reviews",
);
export const Review: Model<IReview> = /* mongoose.models.Review || */ mongoose.model(modelName, CommentSchema, "reviews");

@ -1,10 +1,4 @@
import mongoose, {
Schema,
connect,
PopulatedDoc,
Document,
Model,
} from "mongoose";
import mongoose, { PopulatedDoc, Document, Model } from "mongoose";
import SequenceFactory from "mongoose-sequence";
import bcrypt from "bcryptjs";
import md5 from "blueimp-md5";
@ -274,25 +268,15 @@ UserSchema.static("generateHash", function (password: string): string {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8));
});
UserSchema.methods.validPassword = function (password: string): boolean {
return (
md5(password) === this.password ||
bcrypt.compareSync(password, this.password) ||
false
);
return md5(password) === this.password || bcrypt.compareSync(password, this.password) || false;
};
UserSchema.methods.generateJWT = function (jwtKey: string): string {
let token = jwt.sign(
{ id: this._id, isAdmin: this.profile.isAdmin },
jwtKey,
{
let token = jwt.sign({ id: this._id, isAdmin: this.profile.isAdmin }, jwtKey, {
expiresIn: "14 days",
},
);
});
return token;
};
hasMigrated &&
!mongoose.models.User &&
UserSchema.plugin(AutoIncrement, { id: "userid", inc_field: "_id" });
hasMigrated && !mongoose.models.User && UserSchema.plugin(AutoIncrement, { id: "userid", inc_field: "_id" });
export const User = mongoose.model<IUser, UModel>("User", UserSchema, "users");

@ -1,6 +1,5 @@
<script lang="ts" setup>
import { ref, reactive } from "vue";
import useApiFetch from "../../composables/useApiFetch";
import { reactive } from "vue";
import { notification } from "ant-design-vue";
interface FormState {
username: string;
@ -38,28 +37,11 @@
};
</script>
<template>
<a-form
:model="formState"
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-form :model="formState" 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-form-item>
<a-form-item
:colon="false"
label="Password"
name="password"
:rules="[{ required: true, message: 'Password required!' }]"
>
<a-form-item :colon="false" label="Password" name="password" :rules="[{ required: true, message: 'Password required!' }]">
<a-input-password v-model:value="formState.password" />
</a-form-item>
<a-form-item>

@ -1,21 +1,10 @@
<script lang="ts" setup>
import { ref, reactive } from "vue";
import {
Form as veeForm,
Field as veeField,
useField,
useForm,
useSetFieldValue,
useFormErrors,
useFormValues,
RuleExpression,
} from "vee-validate";
import { Field as veeField, useField, useForm } from "vee-validate";
import * as yup from "yup";
import { useRecaptchaProvider } from "vue-recaptcha";
import { useChallengeV2 } from "vue-recaptcha";
import { notification } from "ant-design-vue";
import { log } from "@server/logger";
import termsOfServices from "~/components/tos.vue";
useRecaptchaProvider();
@ -34,24 +23,11 @@
});
// const { execute } = useChallengeV3('submit');
const vschema = yup.object<FormState>().shape({
username: yup
.string()
.ensure()
.trim()
.min(1)
.required("Username is required!"),
password: yup
.string()
.ensure()
.trim()
.min(8)
.required("Password is required!"),
username: yup.string().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!"),
// recaptcha: yup.string().required('Please verify you are human.'),
agree: yup
.boolean()
.oneOf([true], "Please agree to the terms.")
.required("Please agree to the terms."),
agree: yup.boolean().oneOf([true], "Please agree to the terms.").required("Please agree to the terms."),
});
const dv: FormState = {
username: "",
@ -120,48 +96,26 @@ notification[ "error" ]({
<template>
<form @submit="onFinish">
<vee-field name="username" v-slot="{ field, errorMessage, value }">
<a-form-item
label="Username"
:name="field.name"
:validate-status="!!errorMessage ? 'error' : ''"
:help="errorMessage"
>
<a-form-item label="Username" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
<a-input v-bind="field" v-model:value="field.value" />
</a-form-item>
</vee-field>
<vee-field name="email" v-slot="{ field, errorMessage, value }">
<a-form-item
label="Email address"
:name="field.name"
:validate-status="!!errorMessage ? 'error' : ''"
:help="errorMessage"
>
<a-form-item label="Email address" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
<a-input v-bind="field" v-model:value="field.value" />
</a-form-item>
</vee-field>
<vee-field name="password" v-slot="{ field, errorMessage, value }">
<a-form-item
label="Password"
:name="field.name"
:validate-status="!!errorMessage ? 'error' : ''"
:help="errorMessage"
>
<a-form-item label="Password" :name="field.name" :validate-status="!!errorMessage ? 'error' : ''" :help="errorMessage">
<a-input-password v-bind="field" v-model:value="field.value" />
</a-form-item>
</vee-field>
<a-typography-title :level="4" :style="{ textAlign: 'center' }"
>Terms</a-typography-title
>
<a-typography-title :level="4" :style="{ textAlign: 'center' }">Terms</a-typography-title>
<div class="maxHeightScroller">
<div style="height: 100%">
<terms-of-services />
<vee-field
name="agree"
:unchecked-value="false"
type="checkbox"
v-slot="{ field, value, errorMessage }"
>
<vee-field name="agree" :unchecked-value="false" type="checkbox" v-slot="{ field, value, errorMessage }">
<a-checkbox
@update:checked="
(n) => {
@ -179,9 +133,7 @@ notification[ "error" ]({
<div ref="root" />
<a-row :align="'middle'" justify="center">
<a-col>
<a-button size="large" type="primary" html-type="submit"
>Sign me up!</a-button
>
<a-button size="large" type="primary" html-type="submit">Sign me up!</a-button>
</a-col>
</a-row>
</form>

@ -1,6 +1,4 @@
<script setup lang="ts">
import { IChapter } from "@models/stories/chapter";
import { IStory } from "@models/stories";
import { storyEditMiddleware } from "@client/middleware";
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
import SingleChapter from "~/components/story/create/singleChapter.vue";
@ -13,9 +11,7 @@
const rtr = useRoute();
const {
data: { value: originalStory },
} = await useApiFetch<SingleChapterResult | null>(
`/story/${rtr.params.id}/${rtr.params.cidx}`,
);
} = await useApiFetch<SingleChapterResult | null>(`/story/${rtr.params.id}/${rtr.params.cidx}`);
if (originalStory === null) {
throw createError({
statusCode: 404,
@ -28,8 +24,5 @@
</script>
<template>
<single-chapter
:data="toFormChapter(originalStory!.currentChapter)"
:name="originalStory!.currentChapter.title"
/>
<single-chapter :data="toFormChapter(originalStory!.currentChapter)" :name="originalStory!.currentChapter.title" />
</template>

@ -6,12 +6,12 @@
import { IChapter } from "@models/stories/chapter";
import { storyEditMiddleware } from "@client/middleware";
import { IBand } from "@models/band";
import { IUser } from "@models/user";
const rtr = useRoute();
const {
data: { value: originalStory },
} = await useApiFetch<
({ chapters: (IChapter & { text: string })[] } & IStory) | null
>(`/story/${rtr.params.id}/full`);
} = await useApiFetch<({ chapters: (IChapter & { text: string })[] } & IStory) | null>(`/story/${rtr.params.id}/full`);
if (originalStory === null) {
console.log("IT DOESN'T EXIST DAWG");
throw createError({
@ -20,18 +20,18 @@
});
}
definePageMeta({
middleware: [storyEditMiddleware, "auth"],
middleware: ["auth", storyEditMiddleware],
});
const story: FormStory = {
title: originalStory!.title,
coAuthor: originalStory?.coAuthor ? originalStory.coAuthor._id : null,
coAuthor: originalStory?.coAuthor ? (originalStory.coAuthor as IUser)._id : null,
completed: originalStory!.completed,
chapters: originalStory!.chapters.map((a, i) => ({
...a,
id: a.id,
chapterTitle: a.title,
index: i + 1,
bands: a.bands.map((a) => a._id),
bands: (a.bands as IBand[]).map((a) => a._id),
content: a.text,
uuidKey: v4(),
})),
@ -41,14 +41,6 @@
});
</script>
<template>
<a-typography-title style="text-align: center">
Editing "{{ originalStory?.title }}"
</a-typography-title>
<story-form
:can-draft="false"
:data="story"
:endpoint="`/story/${rtr.params.id}`"
endpoint-method="put"
>
</story-form>
<a-typography-title style="text-align: center"> Editing "{{ originalStory?.title }}" </a-typography-title>
<story-form :can-draft="false" :data="story" :endpoint="`/story/${rtr.params.id}`" endpoint-method="put"> </story-form>
</template>

@ -1,7 +1,6 @@
<script lang="ts" setup>
import { format, formatDistanceToNow, formatRelative } from "date-fns";
import { format, formatDistanceToNow } from "date-fns";
import { IUser } from "@models/user";
import { IStory } from "@models/stories";
import { favourites } from "@client/listActions";
import singleStory from "~/components/listings/singleStory.vue";
import icon from "~/components/icon.vue";
@ -16,10 +15,7 @@
}
const activeKey = ref("main");
const activeFavKey = ref("favs/stories");
const uLastVisit = Date.parse(
(userInfo.value?.lastVisit ||
userInfo.value?.lastLogin) as unknown as string,
);
const uLastVisit = Date.parse((userInfo.value?.lastVisit || userInfo.value?.lastLogin) as unknown as string);
const isSelf = userInfo.value?._id === parseInt(rtr.params.id as string);
useHead({
@ -32,10 +28,7 @@
<div style="height: fit-content">
<a-card-meta style="align-items: center; margin: 0.5em">
<template #avatar>
<a-avatar
:size="75"
:src="`/avatars/${userInfo?.profile.avatar}.png`"
/>
<a-avatar :size="75" :src="`/avatars/${userInfo?.profile.avatar}.png`" />
</template>
<template #title>
<a-typography-title :level="1" style="margin-top: 0.5em">
@ -48,15 +41,9 @@
<template #text> Administrator </template>
</a-badge-ribbon>
</template>
<a-descriptions
:column="2"
:labelStyle="{ fontWeight: 'bold' }"
:colon="false"
>
<a-descriptions :column="2" :labelStyle="{ fontWeight: 'bold' }" :colon="false">
<a-descriptions-item label="Last visit">
<div v-if="userInfo?.banned" style="color: red; font-weight: bold">
BANNED
</div>
<div v-if="userInfo?.banned" style="color: red; font-weight: bold">BANNED</div>
<div v-else-if="userInfo?.profile.hidden">
<i>Unknown</i>
</div>
@ -64,38 +51,23 @@
<span>
{{ format(uLastVisit, "MM/dd/yyyy @ hh:mm:ss a") }}
</span>
<span style="margin-left: 0.6em">
({{ formatDistanceToNow(uLastVisit, { addSuffix: true }) }})
</span>
<span style="margin-left: 0.6em"> ({{ formatDistanceToNow(uLastVisit, { addSuffix: true }) }}) </span>
</div>
</a-descriptions-item>
<a-descriptions-item label="Website">
<a
target="_blank"
:href="`${
userInfo?.profile.website.startsWith('http') ? '' : 'http://'
}${userInfo?.profile.website}`"
>
<a target="_blank" :href="`${userInfo?.profile.website.startsWith('http') ? '' : 'http://'}${userInfo?.profile.website}`">
{{ userInfo?.profile.website }}
</a>
</a-descriptions-item>
<a-descriptions-item label="Blog/Journal">
<a
target="_blank"
:href="`${
userInfo?.profile.blog.startsWith('http') ? '' : 'http://'
}${userInfo?.profile.blog}`"
>
<a target="_blank" :href="`${userInfo?.profile.blog.startsWith('http') ? '' : 'http://'}${userInfo?.profile.blog}`">
{{ userInfo?.profile.blog }}
</a>
</a-descriptions-item>
<a-descriptions-item label="Occupation">
{{ userInfo?.profile.occupation }}
</a-descriptions-item>
<a-descriptions-item
label="Email"
v-if="userInfo?.profile.showEmail || ses?.user?.profile.isAdmin"
>
<a-descriptions-item label="Email" v-if="userInfo?.profile.showEmail || ses?.user?.profile.isAdmin">
{{ userInfo?.email }}
</a-descriptions-item>
</a-descriptions>
@ -129,18 +101,7 @@
<single-story :last="true" :story="item" />
</a-col>
<a-col :span="1" v-if="isSelf">
<a
style="color: red"
@click="
() =>
favourites(
userInfo?.favs.stories || [],
item._id,
true,
'story',
)
"
>
<a style="color: red" @click="() => favourites(userInfo?.favs.stories || [], item._id, true, 'story')">
<icon istyle="regular" name="trash" color="#f00" />
</a>
</a-col>
@ -149,16 +110,10 @@
</a-list>
</a-tab-pane>
<a-tab-pane key="favs/authors" tab="Authors">
<a-list
item-layout="horizontal"
:data-source="userInfo?.favs.authors"
:grid="{ gutter: 16, column: 3 }"
>
<a-list item-layout="horizontal" :data-source="userInfo?.favs.authors" :grid="{ gutter: 16, column: 3 }">
<template #renderItem="{ item }">
<a-list-item style="display: flex; align-items: center">
<a-list-item-meta
style="align-items: center; width: min-content"
>
<a-list-item-meta style="align-items: center; width: min-content">
<template #avatar>
<a-avatar :src="`/avatars/${item.profile.avatar}.png`" />
</template>
@ -170,18 +125,7 @@
</a-list-item-meta>
<template #actions v-if="isSelf">
<span>
<a
style="color: red"
@click="
() =>
favourites(
userInfo?.favs.authors || [],
item._id,
true,
'author',
)
"
>
<a style="color: red" @click="() => favourites(userInfo?.favs.authors || [], item._id, true, 'author')">
<icon istyle="regular" name="trash" color="#f00" />
</a>
</span>

@ -1,5 +1,3 @@
import mongoose from "mongoose";
import * as net from "net";
import plugnplay from "@server/plugnplay";
export default defineNuxtPlugin({
name: "mongo",

@ -1,6 +1,6 @@
import mongoose from "mongoose";
import jwt from "jsonwebtoken";
import { IUser, User } from "@models/user";
import { User } from "@models/user";
import { log } from "@server/logger";
export default eventHandler(async (event) => {
@ -25,8 +25,7 @@ export default eventHandler(async (event) => {
if (!user.auth.emailVerified) {
throw createError({
statusCode: 401,
message:
'Account inactive!<br><a href="/activate/resend">Resend verification</a>?',
message: 'Account inactive!<br><a href="/activate/resend">Resend verification</a>?',
});
}
let tok = user.generateJWT(useRuntimeConfig().jwt);

@ -1,11 +1,5 @@
import jwt from "jsonwebtoken";
import { log } from "@server/logger";
export default eventHandler((event) => {
let ahead = (
getHeaders(event).authorization ||
getCookie(event, "auth:token") ||
""
)?.replace("Bearer ", "");
let ahead = (getHeaders(event).authorization || getCookie(event, "auth:token") || "")?.replace("Bearer ", "");
if (event.context.currentUser) {
return {
token: ahead,

@ -1,6 +1,5 @@
import { messages } from "@server/constants";
import { isAdmin } from "@server/middlewareButNotReally";
import { isLoggedIn } from "@server/middlewareButNotReally";
import { Band, IBand } from "@models/band";
export default eventHandler(async (ev) => {

@ -1,6 +1,5 @@
import { listQuerier } from "@server/dbHelpers";
import { Band } from "@models/band";
import { Story } from "@models/stories";
export default eventHandler(async (event) => {
const params = getRouterParams(event);

@ -1,4 +1,3 @@
import { usernameRegex } from "@server/constants";
import { User } from "@models/user";
export default eventHandler(async (event) => {

@ -1,7 +1,6 @@
import san from "sanitize-html";
import { storyQuerier } from "@server/dbHelpers";
import { isLoggedIn } from "@server/middlewareButNotReally";
import { Story } from "@models/stories";
import { Review } from "@models/stories/review";
export default eventHandler(async (ev) => {

@ -1,16 +1,10 @@
import { Readable } from "stream";
import { Document } from "mongoose";
import { IStory, Story } from "@models/stories";
import { FormStory } from "@client/types/form/story";
import { storyQuerier } from "@server/dbHelpers";
import { isLoggedIn } from "@server/middlewareButNotReally";
import { canModify } from "@server/middlewareButNotReally/storyPrivileges";
import {
bodyHandler,
getBucket,
modelFormChapter,
replaceOrUploadContent,
} from "@server/storyHelpers";
import { bodyHandler, getBucket, modelFormChapter, replaceOrUploadContent } from "@server/storyHelpers";
import { countWords } from "@functions";
import { messages } from "@server/constants";

@ -1,10 +1,8 @@
import { Readable } from "stream";
import san from "sanitize-html";
import { FormStory } from "@client/types/form/story";
import { isLoggedIn } from "@server/middlewareButNotReally";
import { getBucket, bodyHandler, modelFormChapter } from "@server/storyHelpers";
import { Story } from "@models/stories";
import { sanitizeConf } from "@server/constants";
import { countWords } from "@functions";
export default eventHandler(async (ev) => {
@ -24,9 +22,7 @@ export default eventHandler(async (ev) => {
});
for (const c of body.chapters) {
story.chapters.push(modelFormChapter(c));
story.chapters[story.chapters.length - 1].words = countWords(
await bodyHandler(c),
);
story.chapters[story.chapters.length - 1].words = countWords(await bodyHandler(c));
}
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 { User } from "@models/user";

@ -1,7 +1,6 @@
import san from "sanitize-html";
import { weirdToNormalChars } from "weird-to-normal-chars";
import { Profile, MyStuff } from "@client/types/form/myStuff";
import { apiRoot, messages } from "@server/constants";
import { MyStuff } from "@client/types/form/myStuff";
import { apiRoot } from "@server/constants";
import { isLoggedIn } from "@server/middlewareButNotReally";
import { Review } from "@models/stories/review";
import { IUser, User } from "@models/user";
@ -51,14 +50,11 @@ export default eventHandler(async (ev) => {
if (exists) {
throw createError(emsg("username"));
}
let { data: lookup } = await axios.get(
`${apiRoot}/session-sharing/lookup`,
{
let { data: lookup } = await axios.get(`${apiRoot}/session-sharing/lookup`, {
params: {
id: ev.context.currentUser!._id,
},
},
);
});
await axios.put(`${apiRoot}/v3/users/${lookup.value.uid}`, {
body: {

@ -1,4 +1,3 @@
import mongoose from "mongoose";
import { log } from "@server/logger";
import plugnplay from "@server/plugnplay";

@ -1,23 +1,26 @@
import jwt from "jsonwebtoken";
import { log } from "@server/logger";
import { User } from "@models/user";
import { messages } from "@server/constants";
import { AccessToken } from "@models/oauth";
export default defineEventHandler(async (event) => {
let ahead = (
getHeaders(event).authorization ||
getCookie(event, "auth:token") ||
""
)?.replace("Bearer ", "");
// console.log("in here fucknuts", ahead);
// log.debug(`'${ahead}'`, { label: "idk" });
let ahead = (getHeaders(event).authorization || getCookie(event, "auth:token") || "")?.replace("Bearer ", "");
if (ahead) {
let toktok = jwt.verify(
ahead,
// ahead.replace("Bearer ", ""),
useRuntimeConfig().jwt,
) as jwt.JwtPayload;
let toktok: jwt.JwtPayload;
try {
toktok = jwt.verify(ahead, useRuntimeConfig().jwt) as jwt.JwtPayload;
let user = await User.findById(toktok.id as number).exec();
if (user && toktok) event.context.currentUser = user;
// setCookie(event, "auth:token", ahead)
} catch (e) {
const t = await AccessToken.findOne({ 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,4 +1,3 @@
import { Document } from "mongoose";
import { IStory } from "@models/stories";
import { IUser } from "@models/user";
import { Request } from "express";

1
typings/h3.d.ts vendored

@ -1,4 +1,3 @@
import type { H3Event, H3EventContext } from "h3";
import { IFicmas } from "@models/challenges/ficmas";
import { IUser } from "@models/user";
declare module "h3" {