style(*): cleanup unused imports
This commit is contained in:
		
							parent
							
								
									5baa00bbb4
								
							
						
					
					
						commit
						c8e84c909e
					
				| @ -3,6 +3,5 @@ | ||||
| 	"useTabs": true, | ||||
| 	"trailingComma": "all", | ||||
| 	"arrowParens": "always", | ||||
| 	"vueIndentScriptAndStyle": true, | ||||
| 	"editorconfig": true | ||||
| 	"vueIndentScriptAndStyle": true | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								app.vue
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								app.vue
									
									
									
									
									
								
							| @ -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`, | ||||
| 			{ | ||||
| 				method: "post", | ||||
| 				body: values, | ||||
| 			}, | ||||
| 		); | ||||
| 		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) => { | ||||
| 							return value?.split(/\W+/).length > 50 || false; | ||||
| 						}, | ||||
| 					) | ||||
| 					.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`, | ||||
| 		{ | ||||
| 			method: "get", | ||||
| 			query: { | ||||
| 				params: { | ||||
| 					id, | ||||
| 				}, | ||||
| 	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, | ||||
| 		{ | ||||
| 			expiresIn: "14 days", | ||||
| 		}, | ||||
| 	); | ||||
| 	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`, | ||||
| 			{ | ||||
| 				params: { | ||||
| 					id: ev.context.currentUser!._id, | ||||
| 				}, | ||||
| 		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 user = await User.findById(toktok.id as number).exec(); | ||||
| 		if (user && toktok) event.context.currentUser = user; | ||||
| 		// setCookie(event, "auth:token", ahead)
 | ||||
| 		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; | ||||
| 		} 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
									
								
								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 { IUser } from "@models/user"; | ||||
| 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 { IUser } from "@models/user"; | ||||
| declare module "h3" { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user