style(*): edit indents

This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2023-12-29 20:53:29 -05:00
parent c8e84c909e
commit 8f45dbe563
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
41 changed files with 198 additions and 491 deletions

View File

@ -1,6 +1,7 @@
[*.*] root = true
[*]
tab_width = 2 tab_width = 2
indent_size = 1 indent_size = 2
indent_style = tab indent_style = tab
charset = utf-8 charset = utf-8
max_line_length = 160 max_line_length = 160

View File

@ -2,25 +2,17 @@
<template> <template>
<div> <div>
<p> <p>
© Rockfic.com, since 2004. Rockfic.com is in no way associated with any © Rockfic.com, since 2004. Rockfic.com is in no way associated with any band listed on this website. Rockfic.com is entertainment. All stories contained
band listed on this website. Rockfic.com is entertainment. All stories on this site are fictional, which means that while the characters may be loosely based on the public personas of real people, the stories themselves are
contained on this site are fictional, which means that while the completely ungrounded from reality and are in no way meant to reflect the private lives, actual practices, or activities of any persons named. Rockfic.com
characters may be loosely based on the public personas of real people, the will remove a work of fiction if an individual named within requests its removal.<br />
stories themselves are completely ungrounded from reality and are in no
way meant to reflect the private lives, actual practices, or activities of
any persons named. Rockfic.com will remove a work of fiction if an
individual named within requests its removal.<br />
For site problems and/or bugs, contact For site problems and/or bugs, contact
<a style="font-weight: bold" href="mailto:bugs@rockfic.com" <a style="font-weight: bold" href="mailto:bugs@rockfic.com">bugs@rockfic.com</a>.<br />
>bugs@rockfic.com</a
>.<br />
For everything else, contact For everything else, contact
<a href="mailto:admin@rockfic.com">admin@rockfic.com</a>. <a href="mailto:admin@rockfic.com">admin@rockfic.com</a>.
</p> </p>
<b>Copyright Notice</b><br /> <b>Copyright Notice</b><br />
All content on this site is copyright of its respective author. You may not, All content on this site is copyright of its respective author. You may not, except with our express written permission, distribute or commercially exploit
except with our express written permission, distribute or commercially the content. Nor may you transmit it or store it in any other website or other form of electronic retrieval system.
exploit the content. Nor may you transmit it or store it in any other
website or other form of electronic retrieval system.
</div> </div>
</template> </template>

View File

@ -30,11 +30,7 @@
promote: !short, promote: !short,
}, },
}); });
messageApi.success( messageApi.success(`User ${props.user?.username} is now ${short ? "an admin" : "a regular user"}.`);
`User ${props.user?.username} is now ${
short ? "an admin" : "a regular user"
}.`,
);
setTimeout(() => { setTimeout(() => {
showDemote.value = false; showDemote.value = false;
}, 1000); }, 1000);
@ -44,26 +40,15 @@
<template> <template>
<a-space :size="10" direction="vertical"> <a-space :size="10" direction="vertical">
<div> <div>
<a-descriptions <a-descriptions :colon="false" :label-style="{ fontWeight: 'bold' }" :column="1">
:colon="false"
:label-style="{ fontWeight: 'bold' }"
:column="1"
>
<a-descriptions-item label="IP addresses"> <a-descriptions-item label="IP addresses">
<a-list :data-source="user?.ipLog"> <a-list :data-source="user?.ipLog">
<template #renderItem="{ item }"> <template #renderItem="{ item }">
{{ item.ip }}<br /> {{ item.ip }}<br />
<a-typography-title :level="5" <a-typography-title :level="5">Other users with this IP:</a-typography-title>
>Other users with this IP:</a-typography-title
>
<div v-if="commonIps != null"> <div v-if="commonIps != null">
<i v-if="!commonIps[item.ip]?.length"> <i v-if="!commonIps[item.ip]?.length"> No other users share this IP. </i>
No other users share this IP. <a-list v-else :data-source="!!commonIps ? commonIps[item.ip] : []">
</i>
<a-list
v-else
:data-source="!!commonIps ? commonIps[item.ip] : []"
>
<template #renderItem="{ item: otherItem }"> <template #renderItem="{ item: otherItem }">
<nuxt-link :to="`/user/${otherItem._id}`"> <nuxt-link :to="`/user/${otherItem._id}`">
{{ otherItem.username }} {{ otherItem.username }}
@ -83,16 +68,10 @@
<b>{{ user?.profile.isAdmin ? "an admin" : "a regular user" }}</b <b>{{ user?.profile.isAdmin ? "an admin" : "a regular user" }}</b
>. >.
</span> </span>
<a-button <a-button danger v-if="!user?.profile.isAdmin" @click="() => (showDemote = true)">
danger
v-if="!user?.profile.isAdmin"
@click="() => (showDemote = true)"
>
<b>Promote to Admin</b> <b>Promote to Admin</b>
</a-button> </a-button>
<a-button v-else @click="() => (showDemote = true)"> <a-button v-else @click="() => (showDemote = true)"> Demote to regular user </a-button>
Demote to regular user
</a-button>
</a-space> </a-space>
<a-divider /> <a-divider />
<div style="display: flex"> <div style="display: flex">
@ -110,8 +89,7 @@
v-model:open="showBanUnban" v-model:open="showBanUnban"
:title="`${user?.banned ? 'Unban' : 'Ban'} ${user?.username}`" :title="`${user?.banned ? 'Unban' : 'Ban'} ${user?.username}`"
> >
Are you sure you want to {{ `${user?.banned ? "unban" : "ban"}` }} Are you sure you want to {{ `${user?.banned ? "unban" : "ban"}` }} {{ user?.username }}?
{{ user?.username }}?
</a-modal> </a-modal>
<a-modal <a-modal
cancel-text="No" cancel-text="No"
@ -119,20 +97,13 @@
@ok="prodem" @ok="prodem"
@cancel="() => (showDemote = false)" @cancel="() => (showDemote = false)"
v-model:open="showDemote" v-model:open="showDemote"
:title="`${short ? 'Demoting' : 'Promoting'} ${user?.username} ${ :title="`${short ? 'Demoting' : 'Promoting'} ${user?.username} ${!short ? 'to an administrator' : 'to a regular user'}`"
!short ? 'to an administrator' : 'to a regular user'
}`"
> >
<div v-if="!short"> <div v-if="!short">
Are you <b><u>absolutely sure</u></b> you want to Are you <b><u>absolutely sure</u></b> you want to <b>promote this user to an admin</b>?
<b>promote this user to an admin</b>?
<br /> <br />
<a-typography-title :level="5"> <a-typography-title :level="5"> This is a VERY dangerous permission to grant. </a-typography-title>
This is a VERY dangerous permission to grant.
</a-typography-title>
</div>
<div v-else>
Are you sure you want to remove this user as an administrator?
</div> </div>
<div v-else>Are you sure you want to remove this user as an administrator?</div>
</a-modal> </a-modal>
</template> </template>

View File

@ -4,9 +4,7 @@
import { SingleChapterResult } from "@client/types/slightlyDifferentStory"; import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
const props = defineProps<{ endpoint: string }>(); const props = defineProps<{ endpoint: string }>();
const story = inject<SingleChapterResult>("story"); const story = inject<SingleChapterResult>("story");
const { data: reviews } = (await useApiFetch<IReview[]>( const { data: reviews } = (await useApiFetch<IReview[]>(`${props.endpoint}/reviews`)) as unknown as {
`${props.endpoint}/reviews`,
)) as unknown as {
data: IReview[]; data: IReview[];
}; };
</script> </script>

View File

@ -12,20 +12,12 @@
}); });
return unflattened.flat(Infinity).map((a) => ({ value: a, label: a })); return unflattened.flat(Infinity).map((a) => ({ value: a, label: a }));
}); });
const charField = useField<string[]>( const charField = useField<string[]>(fname + "characters", cs.fields.characters as unknown as MaybeRef<RuleExpression<string[]>>);
fname + "characters",
cs.fields.characters as unknown as MaybeRef<RuleExpression<string[]>>,
);
const { value, errorMessage, name: bandName, setValue } = charField; const { value, errorMessage, name: bandName, setValue } = charField;
// setValue([]); // setValue([]);
</script> </script>
<template> <template>
<a-form-item <a-form-item :help="errorMessage" label="Characters" :name="bandName as string" :validate-status="!!errorMessage ? 'error' : undefined">
:help="errorMessage"
label="Characters"
:name="bandName as string"
:validate-status="!!errorMessage ? 'error' : undefined"
>
<a-select mode="multiple" :options="opts" v-model:value="value"> <a-select mode="multiple" :options="opts" v-model:value="value">
<template #removeIcon> <template #removeIcon>
<i class="far fa-circle-x" /> <i class="far fa-circle-x" />

View File

@ -6,22 +6,11 @@
value: a, value: a,
label: a, label: a,
})); }));
const { value, errorMessage, name, setValue } = useField<string[]>( const { value, errorMessage, name, setValue } = useField<string[]>(fname + "genre");
fname + "genre",
);
</script> </script>
<template> <template>
<a-form-item <a-form-item :help="errorMessage" label="Genre(s)" :validate-status="!!errorMessage ? 'error' : undefined">
:help="errorMessage" <a-select :allow-clear="true" :options="opts" v-model:value="value" mode="multiple">
label="Genre(s)"
:validate-status="!!errorMessage ? 'error' : undefined"
>
<a-select
:allow-clear="true"
:options="opts"
v-model:value="value"
mode="multiple"
>
<template #removeIcon> <template #removeIcon>
<i class="far fa-circle-x" /> <i class="far fa-circle-x" />
</template> </template>

View File

@ -12,26 +12,14 @@
}); });
return uf.flat(Infinity).map((a) => ({ value: a, label: a })); return uf.flat(Infinity).map((a) => ({ value: a, label: a }));
}); });
const { fields, push, remove, replace, update } = useFieldArray<string[]>( const { fields, push, remove, replace, update } = useFieldArray<string[]>(fname + "relationships");
fname + "relationships",
);
// replace([]); // replace([]);
</script> </script>
<template> <template>
<a-form-item label="Pairings"> <a-form-item label="Pairings">
<a-row <a-row :gutter="5" :wrap="true" v-for="(field, idx) in fields" :key="field.key">
:gutter="5"
:wrap="true"
v-for="(field, idx) in fields"
:key="field.key"
>
<Field :name="fname + 'relationships' + `[${idx}]`"> <Field :name="fname + 'relationships' + `[${idx}]`">
<a-select <a-select mode="multiple" :options="opts" v-model:value="field.value as string[]" @change="(val) => update(idx, val as string[])">
mode="multiple"
:options="opts"
v-model:value="field.value as string[]"
@change="(val) => update(idx, val as string[])"
>
<template #removeIcon> <template #removeIcon>
<i class="far fa-circle-x" /> <i class="far fa-circle-x" />
</template> </template>

View File

@ -3,9 +3,7 @@
mode="multiple" mode="multiple"
style="width: 100%" style="width: 100%"
placeholder="Please select" placeholder="Please select"
:options=" :options="[...Array(25)].map((_, i) => ({ value: (i + 10).toString(36) + (i + 1) }))"
[...Array(25)].map((_, i) => ({ value: (i + 10).toString(36) + (i + 1) }))
"
@change="handleChange" @change="handleChange"
></a-select> ></a-select>
</template> </template>

View File

@ -8,77 +8,45 @@
</script> </script>
<template> <template>
<a-card style="width: 45%; float: left; margin-right: 1.2em" v-if="!!story"> <a-card style="width: 45%; float: left; margin-right: 1.2em" v-if="!!story">
<a-descriptions <a-descriptions :label-style="{ fontWeight: 'bold' }" :colon="false" :column="1">
:label-style="{ fontWeight: 'bold' }"
:colon="false"
:column="1"
>
<a-descriptions-item label="Author"> <a-descriptions-item label="Author">
<nuxt-link :to="`/user/${story?.author._id}`">{{ <nuxt-link :to="`/user/${story?.author._id}`">{{ story?.author.username }}</nuxt-link>
story?.author.username
}}</nuxt-link>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Bands"> <a-descriptions-item label="Bands">
<div <div class="wrapLong" v-for="(item, index) in story?.currentChapter.bands">
class="wrapLong"
v-for="(item, index) in story?.currentChapter.bands"
>
<span> <span>
<nuxt-link :to="`/band/${item._id}`"> <nuxt-link :to="`/band/${item._id}`">
{{ item.name }} {{ item.name }}
</nuxt-link> </nuxt-link>
{{ {{ (index < story!.currentChapter?.bands.length - 1 && ",&nbsp;") || "" }}
(index < story!.currentChapter?.bands.length - 1 && ",&nbsp;") ||
""
}}
</span> </span>
</div> </div>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Genre(s)"> <a-descriptions-item label="Genre(s)">
<div <div class="wrapLong" v-for="(item, index) in story?.currentChapter.genre">
class="wrapLong"
v-for="(item, index) in story?.currentChapter.genre"
>
<span> <span>
{{ item }} {{ item }}
{{ {{ (index < story!.currentChapter?.genre.length - 1 && ",&nbsp;") || "" }}
(index < story!.currentChapter?.genre.length - 1 && ",&nbsp;") ||
""
}}
</span> </span>
</div> </div>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Relationship(s)"> <a-descriptions-item label="Relationship(s)">
<div <div class="wrapLong" v-for="(item, index) in story?.currentChapter.relationships">
class="wrapLong"
v-for="(item, index) in story?.currentChapter.relationships"
>
<span> <span>
{{ item.join("/") }} {{ item.join("/") }}
{{ {{ (index < story!.currentChapter?.relationships.length - 1 && ",&nbsp;") || "" }}
(index < story!.currentChapter?.relationships.length - 1 &&
",&nbsp;") ||
""
}}
</span> </span>
</div> </div>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Character(s)"> <a-descriptions-item label="Character(s)">
<div class="wrapLong"> <div class="wrapLong">
<span v-for="(item, index) in story?.currentChapter.characters"> <span v-for="(item, index) in story?.currentChapter.characters">
{{ item {{ item }}{{ (index < story!.currentChapter?.characters.length - 1 && ",&nbsp;") || "" }}
}}{{
(index < story!.currentChapter?.characters.length - 1 &&
",&nbsp;") ||
""
}}
</span> </span>
</div> </div>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Rating"> <a-descriptions-item label="Rating">
{{ {{ story?.currentChapter.nsfw ? "Adult" : "Suitable for most audiences" }}
story?.currentChapter.nsfw ? "Adult" : "Suitable for most audiences"
}}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="Summary"> <a-descriptions-item label="Summary">
<div v-html="story?.currentChapter.summary"></div> <div v-html="story?.currentChapter.summary"></div>
@ -86,19 +54,9 @@
<a-descriptions-item label="Date posted"> <a-descriptions-item label="Date posted">
<a-tooltip> <a-tooltip>
<template #title> <template #title>
{{ {{ format(Date.parse(story?.currentChapter.posted as unknown as string), "EEEE, LLL dd yyyy @ hh:mm:ss.SSS aa") }}
format(
Date.parse(story?.currentChapter.posted as unknown as string),
"EEEE, LLL dd yyyy @ hh:mm:ss.SSS aa",
)
}}
</template> </template>
{{ {{ format(Date.parse(story?.currentChapter.posted as unknown as string), "yyyy-MM-dd") }}
format(
Date.parse(story?.currentChapter.posted as unknown as string),
"yyyy-MM-dd",
)
}}
</a-tooltip> </a-tooltip>
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
@ -106,12 +64,7 @@
<div class="stats"> <div class="stats">
<span> <span>
<span class="staticon"> <span class="staticon">
<icon <icon :istyle="!dark ? 'solid' : 'regular'" icolor="#ff2883" :size="12" name="heart" />
:istyle="!dark ? 'solid' : 'regular'"
icolor="#ff2883"
:size="12"
name="heart"
/>
</span> </span>
<span> <span>
{{ story.favs }} {{ story.favs }}
@ -119,12 +72,7 @@
</span> </span>
<span> <span>
<span class="staticon"> <span class="staticon">
<icon <icon :istyle="!dark ? 'solid' : 'regular'" icolor="#1787d7" :size="12" name="book-open" />
:istyle="!dark ? 'solid' : 'regular'"
icolor="#1787d7"
:size="12"
name="book-open"
/>
</span> </span>
<span> <span>
{{ story.views }} {{ story.views }}
@ -132,12 +80,7 @@
</span> </span>
<span> <span>
<span class="staticon"> <span class="staticon">
<icon <icon :istyle="!dark ? 'solid' : 'regular'" icolor="#51e07c" :size="12" name="thumbs-up" />
:istyle="!dark ? 'solid' : 'regular'"
icolor="#51e07c"
:size="12"
name="thumbs-up"
/>
</span> </span>
<span> <span>
{{ story.recs }} {{ story.recs }}
@ -145,12 +88,7 @@
</span> </span>
<span> <span>
<span class="staticon"> <span class="staticon">
<icon <icon :istyle="!dark ? 'solid' : 'regular'" icolor="#c2d420" :size="12" name="download" />
:istyle="!dark ? 'solid' : 'regular'"
icolor="#c2d420"
:size="12"
name="download"
/>
</span> </span>
<span> <span>
{{ story.downloads }} {{ story.downloads }}

View File

@ -2,95 +2,59 @@
<template> <template>
<div> <div>
<h3>Age Policy</h3> <h3>Age Policy</h3>
You must be 18 years of age or older to have an account. If you are found to You must be 18 years of age or older to have an account. If you are found to be underage, your account will be suspended without notice.
be underage, your account will be suspended without notice.
<h3>General</h3> <h3>General</h3>
<ol> <ol>
<li>Rockfic.com is not affiliated with any band listed on the site;</li> <li>Rockfic.com is not affiliated with any band listed on the site;</li>
<li> <li>
All stories are fictional and for entertainment purposes only, which All stories are fictional and for entertainment purposes only, which means that while the characters may be loosely based on the public personas of real
means that while the characters may be loosely based on the public people, the stories are completely ungrounded from reality and are in no way meant to reflect the private lives, actual practices, or activities of any
personas of real people, the stories are completely ungrounded from persons named;
reality and are in no way meant to reflect the private lives, actual
practices, or activities of any persons named;
</li> </li>
<li>Rockfic.com will remove a work of fiction if an individual named within requests its removal;</li>
<li>We do not sell, trade or otherwise disclose personal user information to any third party;</li>
<li> <li>
Rockfic.com will remove a work of fiction if an individual named within We reserve the right to access and disclose individually identifiable information to comply with any legal obligation or governmental request to enforce
requests its removal; or apply our Terms of Use, or to ensure the constitutional rights and safety of Rockfic.com users.
</li>
<li>
We do not sell, trade or otherwise disclose personal user information to
any third party;
</li>
<li>
We reserve the right to access and disclose individually identifiable
information to comply with any legal obligation or governmental request
to enforce or apply our Terms of Use, or to ensure the constitutional
rights and safety of Rockfic.com users.
</li> </li>
</ol> </ol>
<h3>Content</h3> <h3>Content</h3>
<ol> <ol>
<li> <li>Rockfic.com does not own any of the stories published on the site, nor are its administrators legally accountable for its content;</li>
Rockfic.com does not own any of the stories published on the site, nor
are its administrators legally accountable for its content;
</li>
<li>Authors are the sole copyright owners of their stories;</li> <li>Authors are the sole copyright owners of their stories;</li>
<li>Authors are responsible for managing any content they create; this includes managing the privacy settings facilitated by the site;</li>
<li> <li>
Authors are responsible for managing any content they create; this Rockfic.com reserves the right to remove content, including anything we consider offensive, inappropriate or defamatory, at our discretion; any content
includes managing the privacy settings facilitated by the site; we decide to remove will be done in accordance with our Submission Rules;
</li>
<li>Rockfic.com reserves the right to edit content, in line with our Submission Rules.</li>
<li>
If you believe that you own the copyright in any of the content on Rockfic.com, and you have not been recognized as the copyright owner, please contact
us and your case will be investigated;
</li> </li>
<li> <li>
Rockfic.com reserves the right to remove content, including anything we While we investigate reports of inappropriate content or copyright issues, we may temporarily remove the content in question; if we agree that you are
consider offensive, inappropriate or defamatory, at our discretion; any the copyright owner or that the content is inappropriate, we will remove the relevant content permanently.
content we decide to remove will be done in accordance with our
Submission Rules;
</li>
<li>
Rockfic.com reserves the right to edit content, in line with our
Submission Rules.
</li>
<li>
If you believe that you own the copyright in any of the content on
Rockfic.com, and you have not been recognized as the copyright owner,
please contact us and your case will be investigated;
</li>
<li>
While we investigate reports of inappropriate content or copyright
issues, we may temporarily remove the content in question; if we agree
that you are the copyright owner or that the content is inappropriate,
we will remove the relevant content permanently.
</li> </li>
</ol> </ol>
<h3>Conduct</h3> <h3>Conduct</h3>
<ol> <ol>
<li> <li>Rockfic.com operates a strict anti-bullying and anti-harassment policy; if you have an issue to report, contact us;</li>
Rockfic.com operates a strict anti-bullying and anti-harassment policy;
if you have an issue to report, contact us;
</li>
<li> <li>
Rockfic.com will permanently ban users who: Rockfic.com will permanently ban users who:
<ol style="list-style-type: lower-alpha"> <ol style="list-style-type: lower-alpha">
<li> <li>break the law, for example by saying something libellous, or by posting something which results in a criminal offence;</li>
break the law, for example by saying something libellous, or by
posting something which results in a criminal offence;
</li>
<li>share the personal details of users without their permission;</li> <li>share the personal details of users without their permission;</li>
<li>impersonate another user;</li> <li>impersonate another user;</li>
<li> <li>collect or use any information from Rockfic.com with the intent to harm, discredit or harass any other user; or</li>
collect or use any information from Rockfic.com with the intent to
harm, discredit or harass any other user; or
</li>
<li>do anything which impacts the performance of the site.</li> <li>do anything which impacts the performance of the site.</li>
</ol> </ol>
</li> </li>
</ol> </ol>
<h3>Copyright</h3> <h3>Copyright</h3>
All content on this site is copyright of its respective author. You may not, All content on this site is copyright of its respective author. You may not, except with our express written permission, distribute or commercially exploit
except with our express written permission, distribute or commercially the content. Nor may you transmit it or store it in any other website or other form of electronic retrieval system.
exploit the content. Nor may you transmit it or store it in any other
website or other form of electronic retrieval system.
</div> </div>
</template> </template>

View File

@ -60,8 +60,7 @@ export const fancy = {
items: "h1 h2 h3 h4 h5 h6", items: "h1 h2 h3 h4 h5 h6",
}, },
}, },
toolbar: toolbar: "undo redo | paste | bold italic underline | hr image link | forecolor styles | heading alignment | code",
"undo redo | paste | bold italic underline | hr image link | forecolor styles | heading alignment | code",
contextmenu: "bold italic underline | hr | link | image | paste", contextmenu: "bold italic underline | hr | link | image | paste",
external_plugins: { external_plugins: {
mentions: "/plugins/mentions/plugin.min.js", mentions: "/plugins/mentions/plugin.min.js",
@ -122,11 +121,7 @@ export const story = {
`advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table advcode help wordcount save`.split( `advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table advcode help wordcount save`.split(
" ", " ",
), ),
toolbar: toolbar: "undo redo | paste |" + "bold italic underline | hr | alignleft aligncenter " + "alignright alignjustify | " + "| code",
"undo redo | paste |" +
"bold italic underline | hr | alignleft aligncenter " +
"alignright alignjustify | " +
"| code",
contextmenu: "bold italic underline | hr | paste | link", contextmenu: "bold italic underline | hr | paste | link",
}; };
export const bare = { export const bare = {

View File

@ -3,12 +3,7 @@ import { FavPayload, HidePayload, SubPayload } from "./types/form/favSub";
const base = `/user/me`; const base = `/user/me`;
export const favourites = ( export const favourites = (values: (any & { _id: number })[], id: number, remove: boolean, type: "story" | "author") => {
values: (any & { _id: number })[],
id: number,
remove: boolean,
type: "story" | "author",
) => {
values?.splice( values?.splice(
values!.findIndex((a) => a._id == id), values!.findIndex((a) => a._id == id),
1, 1,
@ -26,12 +21,7 @@ export const favourites = (
}); });
}; };
export const subscriptions = ( export const subscriptions = (values: (any & { _id: number })[], id: number, action: "hide" | "subscribe" | "unsubscribe", type: "bands" | "authors") => {
values: (any & { _id: number })[],
id: number,
action: "hide" | "subscribe" | "unsubscribe",
type: "bands" | "authors",
) => {
values?.splice( values?.splice(
values!.findIndex((a) => a._id == id), values!.findIndex((a) => a._id == id),
1, 1,

View File

@ -3,15 +3,16 @@ import { IChapter } from "@models/stories/chapter";
import { IStory } from "@models/stories"; import { IStory } from "@models/stories";
import { messages } from "@server/constants"; import { messages } from "@server/constants";
import { IUser } from "@models/user"; import { IUser } from "@models/user";
import { IDraft } from "@models/stories/draft";
const show404 = () => showError({ statusCode: 404, message: messages[404] });
export const storyMiddleware = defineNuxtRouteMiddleware(async (to, from) => { export const storyMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
const { getSession } = useAuth(); const { getSession } = useAuth();
await getSession({ force: true }); await getSession({ force: true });
const { data } = useAuth(); const { data } = useAuth();
console.log("to n from", to, from, data); console.log("to n from", to, from, data);
const { data: story, error } = await useApiFetch<SingleChapterResult>( const { data: story, error } = await useApiFetch<SingleChapterResult>(to.path);
to.path,
);
if (error.value) { if (error.value) {
return showError(error.value); return showError(error.value);
} else if (!story.value) { } else if (!story.value) {
@ -23,24 +24,27 @@ export const storyMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
} }
}); });
export const storyEditMiddleware = defineNuxtRouteMiddleware( export const storyEditMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
async (to, from) => { const { data: curU } = useAuth();
const { data: curU } = useAuth(); const rtr = useRoute();
const rtr = useRoute(); const { data: storyInfo } = await useApiFetch<({ chapters: (IChapter & { text: string })[] } & IStory) | null>(`/story/${rtr.params.id}/full`);
const { data: storyInfo } = await useApiFetch< if (!storyInfo.value) show404();
({ chapters: (IChapter & { text: string })[] } & IStory) | null if (curU.value?.user?._id !== (storyInfo.value?.author as IUser)._id && curU.value?.user?._id !== (storyInfo.value?.coAuthor as IUser)?._id) {
>(`/story/${rtr.params.id}/full`); return showError({
if (!storyInfo.value) { statusCode: 403,
return showError({ statusCode: 404, message: messages[404] }); message: messages[403],
} });
if ( }
curU.value?.user?._id !== (storyInfo.value?.author as IUser)._id && });
curU.value?.user?._id !== (storyInfo.value?.coAuthor as IUser)?._id export const draftEditMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
) { const { data: curU } = useAuth();
return showError({ const rtr = useRoute();
statusCode: 403, const { data: storyInfo } = await useApiFetch<IDraft | null>(`/draft/${rtr.params.id}`);
message: messages[403], if (!storyInfo.value) show404();
}); if (curU.value?.user?._id !== (storyInfo.value?.author as IUser)._id && curU.value?.user?._id !== (storyInfo.value?.coAuthor as IUser)?._id) {
} return showError({
}, statusCode: 403,
); message: messages[403],
});
}
});

View File

@ -0,0 +1,3 @@
export interface DraftInfo {
id: number;
}

View File

@ -1,4 +1,6 @@
import { V4Options, v4 } from "uuid"; import { v4 } from "uuid";
import { IChapter } from "@models/stories/chapter";
import { IBand } from "@models/band";
export interface FormChapter { export interface FormChapter {
id?: number; id?: number;
@ -51,3 +53,21 @@ export const defaultStory: FormStory = {
challenge: null, challenge: null,
completed: false, completed: false,
}; };
export function toFormChapter(chap: IChapter & { text: string }): FormChapter {
return {
chapterTitle: chap.title,
index: 1,
summary: chap.summary,
notes: chap.notes,
genre: chap.genre,
bands: (chap.bands as IBand[]).map((a) => a._id),
characters: chap.characters,
relationships: chap.relationships,
nsfw: chap.nsfw,
loggedInOnly: chap.loggedInOnly,
hidden: chap.hidden,
content: chap.text,
uuidKey: v4(),
};
}

View File

@ -27,11 +27,7 @@ export const autoSave = async (values: any) => {
} }
}; };
export const autoEdit = ( export const autoEdit = (values: any, endpoint: string, method: "put" | "post") => {
values: any,
endpoint: string,
method: "put" | "post",
) => {
const [messageApi, contextHolder] = message.useMessage(); const [messageApi, contextHolder] = message.useMessage();
useApiFetch<{ success: boolean; data: IStory }>(endpoint, { useApiFetch<{ success: boolean; data: IStory }>(endpoint, {
method, method,

View File

@ -1,10 +1,8 @@
import turndown from "turndown"; import turndown from "turndown";
export const ContentFilenameRegex = /\.(doc|docx|md|markdown)$/i; export const ContentFilenameRegex = /\.(doc|docx|md|markdown)$/i;
export const emailRegex: RegExp = export const emailRegex: RegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; export const usernameRegex: (uname: string) => RegExp = (uname: string) => new RegExp("^" + uname.trim().replace(/\*/g, "\\*") + "$", "i");
export const usernameRegex: (uname: string) => RegExp = (uname: string) =>
new RegExp("^" + uname.trim().replace(/\*/g, "\\*") + "$", "i");
export const mammothTemplate = (doc, defaults, content) => { export const mammothTemplate = (doc, defaults, content) => {
return content.replace(/\n|\r\n|\r/gm, ""); return content.replace(/\n|\r\n|\r/gm, "");
}; };
@ -90,17 +88,7 @@ export const sanitizeConf = {
img: ["src"], img: ["src"],
}, },
// Lots of these won't come up by default because we don't allow them // Lots of these won't come up by default because we don't allow them
selfClosing: [ selfClosing: ["img", "br", "hr", "area", "base", "basefont", "input", "link", "meta"],
"img",
"br",
"hr",
"area",
"base",
"basefont",
"input",
"link",
"meta",
],
// URL schemes we permit // URL schemes we permit
allowedSchemes: ["http", "https", "ftp", "mailto", "tel"], allowedSchemes: ["http", "https", "ftp", "mailto", "tel"],
allowedSchemesAppliedToAttributes: ["href", "src", "cite"], allowedSchemesAppliedToAttributes: ["href", "src", "cite"],

View File

@ -0,0 +1,20 @@
import { Document } from "mongoose";
import { IDraft } from "@models/stories/draft";
import { EventHandlerRequest, H3Event } from "h3";
import { IChapter } from "@models/stories/chapter";
import getDraftBucket from "@server/storyHelpers/getDraftBucket";
import { norm, stringifyStream } from "@functions";
export interface HydratedDraft extends Omit<IDraft, "chapters"> {
chapters: (IChapter & { text: string })[];
}
export default async function (draft: Document<number, {}, IDraft> & IDraft, event: H3Event<EventHandlerRequest>): Promise<HydratedDraft> {
const finObj = draft.toObject() as HydratedDraft;
const bucket = getDraftBucket();
for (let chap of finObj.chapters) {
let dstream = bucket.openDownloadStreamByName(`/drafts/${chap.id}.txt`);
chap.text = norm(await stringifyStream(dstream));
}
return finObj;
}

View File

@ -20,7 +20,6 @@ export default async function (ev: H3Event<EventHandlerRequest>) {
}) })
.populate({ path: "challenge", model: Challenge }) .populate({ path: "challenge", model: Challenge })
.exec(); .exec();
if (story == null) if (story == null) throw createError({ statusCode: 404, message: "Not found." });
throw createError({ statusCode: 404, message: "Not found." });
return story; return story;
} }

View File

@ -1,8 +1,4 @@
export const submissionsOpen = () => export const submissionsOpen = () =>
new Date() < new Date(Date.parse(`Dec 24 ${new Date().getFullYear()}`)) && new Date() < new Date(Date.parse(`Dec 24 ${new Date().getFullYear()}`)) && Date.now() > Date.parse(`Nov 1 ${new Date().getFullYear()}`);
Date.now() > Date.parse(`Nov 1 ${new Date().getFullYear()}`); export const ficsHidden = (d: number) => d < Date.parse(`Dec 25 ${new Date().getFullYear()}`);
export const ficsHidden = (d: number) => export const status = () => Date.now() > Date.parse(`Oct 1 ${new Date().getFullYear()}`) && Date.now() < Date.parse(`Nov 30 ${new Date().getFullYear()}`);
d < Date.parse(`Dec 25 ${new Date().getFullYear()}`);
export const status = () =>
Date.now() > Date.parse(`Oct 1 ${new Date().getFullYear()}`) &&
Date.now() < Date.parse(`Nov 30 ${new Date().getFullYear()}`);

View File

@ -5,25 +5,13 @@ import { IDraft } from "@models/stories/draft";
import { IUser } from "@models/user"; import { IUser } from "@models/user";
export function canDelete(event: H3Event<EventHandlerRequest>, story: IStory) { export function canDelete(event: H3Event<EventHandlerRequest>, story: IStory) {
isLoggedIn(event); isLoggedIn(event);
return ( return event.context.currentUser?.profile.isAdmin || (story.author as IUser)._id === event.context.currentUser?._id;
event.context.currentUser?.profile.isAdmin ||
(story.author as IUser)._id === event.context.currentUser?._id
);
} }
export function canDeleteDraft( export function canDeleteDraft(event: H3Event<EventHandlerRequest>, story: IDraft) {
event: H3Event<EventHandlerRequest>,
story: IDraft,
) {
isLoggedIn(event); isLoggedIn(event);
return story.author === event.context.currentUser?._id; return story.author === event.context.currentUser?._id;
} }
export function canModify( export function canModify(event: H3Event<EventHandlerRequest>, story: IStory | IDraft) {
event: H3Event<EventHandlerRequest>,
story: IStory | IDraft,
) {
isLoggedIn(event); isLoggedIn(event);
return ( return event.context.currentUser?._id === (story.author as IUser)._id || (story.coAuthor as IUser)?._id === event.context.currentUser?._id;
event.context.currentUser?._id === (story.author as IUser)._id ||
(story.coAuthor as IUser)?._id === event.context.currentUser?._id
);
} }

View File

@ -14,17 +14,9 @@ export default async function (bodyObj: FormChapter): Promise<string> {
str = bodyObj.content; str = bodyObj.content;
} else if (bodyObj.file) { } else if (bodyObj.file) {
let ext = extname(bodyObj.file).toLowerCase(); let ext = extname(bodyObj.file).toLowerCase();
if (ext === ".md" || ext === ".markdown") if (ext === ".md" || ext === ".markdown") str = marked.parse(readFileSync(resolve(`tmp/${bodyObj.file}`)).toString());
str = marked.parse(
readFileSync(resolve(`tmp/${bodyObj.file}`)).toString(),
);
else if (ext === ".doc" || ext === ".docx") else if (ext === ".doc" || ext === ".docx")
str = ( str = (await mammoth.convertToHtml({ path: resolve(`tmp/${bodyObj.file}`) }, { styleMap: ["b => b", "i => i", "u => u"] })).value;
await mammoth.convertToHtml(
{ path: resolve(`tmp/${bodyObj.file}`) },
{ styleMap: ["b => b", "i => i", "u => u"] },
)
).value;
else else
throw createError({ throw createError({
statusCode: 400, statusCode: 400,

View File

@ -1,9 +1,6 @@
import getBucket from "./getBucket"; import getBucket from "./getBucket";
import { Readable } from "stream"; import { Readable } from "stream";
export default async function replaceGridFS( export default async function replaceGridFS(chapterID: number | undefined, content: string) {
chapterID: number | undefined,
content: string,
) {
let filename = `/stories/${chapterID}.txt`; let filename = `/stories/${chapterID}.txt`;
const bucket = getBucket(); const bucket = getBucket();
if (chapterID) { if (chapterID) {

View File

@ -4,9 +4,7 @@
import { subscriptions, bp } from "@client/listActions"; import { subscriptions, bp } from "@client/listActions";
import { IUser } from "@models/user"; import { IUser } from "@models/user";
const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>( const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>("/band/all")) as unknown as { data: Ref<IBand[]> };
"/band/all",
)) as unknown as { data: Ref<IBand[]> };
const { data: rd }: { data: any } = useAuth(); const { data: rd }: { data: any } = useAuth();
const data = rd as { user: IUser }; const data = rd as { user: IUser };
@ -29,16 +27,10 @@
</a-col> </a-col>
<!-- subscribe... --> <!-- subscribe... -->
<a-col v-if="data && data.user?._id" style="margin-left: auto"> <a-col v-if="data && data.user?._id" style="margin-left: auto">
<a <a v-if="!data?.user.subscriptions.bands.includes(item._id)" @click="(e) => hider(bands, item._id, 'subscribe', 'bands')">
v-if="!data?.user.subscriptions.bands.includes(item._id)"
@click="(e) => hider(bands, item._id, 'subscribe', 'bands')"
>
<icon :istyle="'regular'" name="paper-plane" :size="12" /> <icon :istyle="'regular'" name="paper-plane" :size="12" />
</a> </a>
<a <a v-else @click="(e) => hider(bands, item._id, 'unsubscribe', 'bands')">
v-else
@click="(e) => hider(bands, item._id, 'unsubscribe', 'bands')"
>
<icon :istyle="'regular'" name="x" :size="12" /> <icon :istyle="'regular'" name="x" :size="12" />
</a> </a>
</a-col> </a-col>

View File

@ -1,8 +1,5 @@
<template> <template>
<div style="width: 100%; height: 90vh"> <div style="width: 100%; height: 90vh">
<iframe <iframe style="width: 100%; height: 100%" src="https://www.rockfic.com/forum/" />
style="width: 100%; height: 100%"
src="https://www.rockfic.com/forum/"
/>
</div> </div>
</template> </template>

View File

@ -10,10 +10,5 @@
</script> </script>
<template> <template>
<a-typography-title> Post a new Story </a-typography-title> <a-typography-title> Post a new Story </a-typography-title>
<story-form <story-form endpoint-method="post" :can-draft="true" :data="defaultStory" endpoint="/story/new" />
endpoint-method="post"
:can-draft="true"
:data="defaultStory"
endpoint="/story/new"
/>
</template> </template>

View File

@ -12,9 +12,7 @@
middleware: [storyMiddleware], middleware: [storyMiddleware],
}); });
const rtr = useRoute(); const rtr = useRoute();
const { data: story, error } = await useApiFetch<SingleChapterResult>( const { data: story, error } = await useApiFetch<SingleChapterResult>(`/story/${rtr.params.id}/${rtr.params.cidx}`);
`/story/${rtr.params.id}/${rtr.params.cidx}`,
);
provide<SingleChapterResult | null>("story", story.value); provide<SingleChapterResult | null>("story", story.value);
console.log("storyyy", story.value?.currentChapter); console.log("storyyy", story.value?.currentChapter);
console.log(rtr); console.log(rtr);
@ -52,55 +50,24 @@
<div v-html="story?.currentChapter.text"></div> <div v-html="story?.currentChapter.text"></div>
<a-divider style="background-color: #fff" /> <a-divider style="background-color: #fff" />
<a-button-group size="large" v-if="story.totalChapters > 1"> <a-button-group size="large" v-if="story.totalChapters > 1">
<a-button <a-button v-if="parseInt(rtr.params.cidx as string) > 1" @click="() => navigateTo(`/story/${rtr.params.id}/1`)"> First </a-button>
v-if="parseInt(rtr.params.cidx as string) > 1" <a-button v-if="parseInt(rtr.params.cidx as string) > 1" @click="() => navigateTo(`/story/${rtr.params.id}/${parseInt(rtr.params.cidx as string) - 1}`)">
@click="() => navigateTo(`/story/${rtr.params.id}/1`)"
>
First
</a-button>
<a-button
v-if="parseInt(rtr.params.cidx as string) > 1"
@click="
() =>
navigateTo(
`/story/${rtr.params.id}/${
parseInt(rtr.params.cidx as string) - 1
}`,
)
"
>
Previous Previous
</a-button> </a-button>
<a-button <a-button
v-if=" v-if="parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1"
parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1 @click="() => navigateTo(`/story/${rtr.params.id}/${parseInt(rtr.params.cidx as string) + 1}`)"
"
@click="
() =>
navigateTo(
`/story/${rtr.params.id}/${
parseInt(rtr.params.cidx as string) + 1
}`,
)
"
> >
Next Next
</a-button> </a-button>
<a-button <a-button
@click=" @click="() => navigateTo(`/story/${rtr.params.id}/${story.chapterNames.length}`)"
() => v-if="parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1"
navigateTo(`/story/${rtr.params.id}/${story.chapterNames.length}`)
"
v-if="
parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1
"
> >
Last Last
</a-button> </a-button>
</a-button-group> </a-button-group>
<a-typography-title style="text-align: center" :level="2"> <a-typography-title style="text-align: center" :level="2"> Reviews </a-typography-title>
Reviews
</a-typography-title>
<for-chapter :endpoint="`/story/${rtr.params.id}/${rtr.params.cidx}`" /> <for-chapter :endpoint="`/story/${rtr.params.id}/${rtr.params.cidx}`" />
</div> </div>
</template> </template>

View File

@ -6,9 +6,7 @@
middleware: [storyMiddleware], middleware: [storyMiddleware],
}); });
const rtr = useRoute(); const rtr = useRoute();
const { data: story, error } = await useApiFetch<IStory>( const { data: story, error } = await useApiFetch<IStory>(`/story/${rtr.params.id}`);
`/story/${rtr.params.id}`,
);
</script> </script>
<template> <template>

View File

@ -15,10 +15,7 @@ export default eventHandler(async (event) => {
await captcha(event); await captcha(event);
console.log("fields exist"); console.log("fields exist");
const user = await User.findOne({ const user = await User.findOne({
$or: [ $or: [{ username: usernameRegex(body.username) }, { email: (body.email as string).toLowerCase() }],
{ username: usernameRegex(body.username) },
{ email: (body.email as string).toLowerCase() },
],
}); });
console.log("after f0", user); console.log("after f0", user);
if (user) if (user)
@ -27,7 +24,7 @@ export default eventHandler(async (event) => {
message: "A user with that username or email already exists.", message: "A user with that username or email already exists.",
}); });
let nuser = new User({ let nuser = new User({
email: body.email.toLowerCase(), email: body.email.toLowerCase().trim(),
username: w2nc(body.username.trim()), username: w2nc(body.username.trim()),
password: User.generateHash(body.password), password: User.generateHash(body.password),
auth: { auth: {

View File

@ -14,10 +14,7 @@ let authorSingleton: {
const threshold = 60 * 60 * 1000; const threshold = 60 * 60 * 1000;
export default cachedEventHandler( export default cachedEventHandler(
async (ev) => { async (ev) => {
if ( if (Date.now() - authorSingleton.lastRefreshed >= threshold || authorSingleton.data.length < 1)
Date.now() - authorSingleton.lastRefreshed >= threshold ||
authorSingleton.data.length < 1
)
authorSingleton.data = await User.aggregate([ authorSingleton.data = await User.aggregate([
{ $project: { username: true, _id: true } }, { $project: { username: true, _id: true } },
{ {
@ -38,10 +35,8 @@ export default cachedEventHandler(
}, },
]); ]);
authorSingleton.data.sort((a, b) => { authorSingleton.data.sort((a, b) => {
if (a.username.toLocaleUpperCase() > b.username.toLocaleUpperCase()) if (a.username.toLocaleUpperCase() > b.username.toLocaleUpperCase()) return 1;
return 1; else if (a.username.toLocaleUpperCase() < b.username.toLocaleUpperCase()) return -1;
else if (a.username.toLocaleUpperCase() < b.username.toLocaleUpperCase())
return -1;
return 0; return 0;
}); });
return authorSingleton.data; return authorSingleton.data;

View File

@ -19,10 +19,7 @@ export default eventHandler(async (ev) => {
statusCode: 400, statusCode: 400,
message: "bad parameter", message: "bad parameter",
}); });
if ( if (ev.context.currentUser!._id != s2v?.author && ev.context.currentUser!._id != c2d._id)
ev.context.currentUser!._id != s2v?.author &&
ev.context.currentUser!._id != c2d._id
)
throw createError({ throw createError({
statusCode: 403, statusCode: 403,
message: messages[403], message: messages[403],

View File

@ -4,9 +4,7 @@ import { isIdNan } from "@server/middlewareButNotReally";
export default eventHandler(async (ev) => { export default eventHandler(async (ev) => {
const revid = isIdNan(ev); const revid = isIdNan(ev);
const r = await Review.findById(revid) const r = await Review.findById(revid).populate("author", "username _id").exec();
.populate("author", "username _id")
.exec();
if (!r) { if (!r) {
throw createError({ throw createError({
statusCode: 404, statusCode: 404,

View File

@ -32,8 +32,6 @@ export default eventHandler(async (ev) => {
}); });
return { return {
success: true, success: true,
data: await Review.findById(revid) data: await Review.findById(revid).populate("author", "username profile _id").exec(),
.populate("author", "username profile _id")
.exec(),
}; };
}); });

View File

@ -20,9 +20,7 @@ export default eventHandler(async (ev) => {
}); });
} }
if ( if (
(replyingTo?.author as IUser).blocked.includes( (replyingTo?.author as IUser).blocked.includes(ev.context.currentUser!._id) ||
ev.context.currentUser!._id,
) ||
ev.context.currentUser!.blocked.includes((replyingTo?.author as IUser)._id) ev.context.currentUser!.blocked.includes((replyingTo?.author as IUser)._id)
) { ) {
throw createError({ throw createError({
@ -40,9 +38,7 @@ export default eventHandler(async (ev) => {
datePosted: new Date(), datePosted: new Date(),
}); });
const { _id } = await newReply.save(); const { _id } = await newReply.save();
const nrs = (await Review.findOne({ _id }) const nrs = (await Review.findOne({ _id }).populate("author", "username _id blocked").exec())!;
.populate("author", "username _id blocked")
.exec())!;
replyingTo.replies.push(nrs._id); replyingTo.replies.push(nrs._id);
await replyingTo.save(); await replyingTo.save();
const story = await Story.findById(replyingTo.leftOn); const story = await Story.findById(replyingTo.leftOn);
@ -52,9 +48,7 @@ export default eventHandler(async (ev) => {
}); });
} }
return { return {
back: `/story/${replyingTo.leftOn}/${ back: `/story/${replyingTo.leftOn}/${story!.chapters.findIndex((x) => x.id === nrs.whichChapter) + 1}`,
story!.chapters.findIndex((x) => x.id === nrs.whichChapter) + 1
}`,
data: nrs.toObject(), data: nrs.toObject(),
success: true, success: true,
}; };

View File

@ -8,11 +8,7 @@ export default eventHandler(async (ev) => {
isLoggedIn(ev); isLoggedIn(ev);
const s = await storyQuerier(ev); const s = await storyQuerier(ev);
const hidden = s.chapters.some((a) => a.hidden); const hidden = s.chapters.some((a) => a.hidden);
if ( if (hidden && ev.context.currentUser?._id !== (s.author as IUser)._id && !ev.context.currentUser?.profile.isAdmin) {
hidden &&
ev.context.currentUser?._id !== (s.author as IUser)._id &&
!ev.context.currentUser?.profile.isAdmin
) {
throw createError({ throw createError({
statusCode: 403, statusCode: 403,
message: messages[403], message: messages[403],

View File

@ -20,10 +20,7 @@ export default cachedEventHandler(
async (event) => { async (event) => {
let aa = mongoose.connection.db.collection("z_index_totAuthors"); let aa = mongoose.connection.db.collection("z_index_totAuthors");
let totalStories = await Story.countDocuments({ "chapters.hidden": false }); let totalStories = await Story.countDocuments({ "chapters.hidden": false });
if ( if (!authorSingleton.data.length || Date.now() - authorSingleton.lastRefreshed >= threshold) {
!authorSingleton.data.length ||
Date.now() - authorSingleton.lastRefreshed >= threshold
) {
authorSingleton.data = await User.aggregate([ authorSingleton.data = await User.aggregate([
{ $project: { username: true, _id: true } }, { $project: { username: true, _id: true } },
{ {

View File

@ -16,9 +16,5 @@ export default eventHandler(async (ev) => {
}) })
.populate("story") .populate("story")
.exec(); .exec();
return ar return ar.map((a) => a.toObject()).sort((a, b) => b.datePosted.getMilliseconds() - a.datePosted.getMilliseconds());
.map((a) => a.toObject())
.sort(
(a, b) => b.datePosted.getMilliseconds() - a.datePosted.getMilliseconds(),
);
}); });

View File

@ -1,10 +1,7 @@
export default eventHandler(async (ev) => { export default eventHandler(async (ev) => {
if (ev.context.currentUser) { if (ev.context.currentUser) {
let log = ev.context.currentUser.ipLog; let log = ev.context.currentUser.ipLog;
if ( if (ev.context.clientAddress !== undefined && !/127\.0\.0\.1|localhost|::1/.test(ev.context.clientAddress)) {
ev.context.clientAddress !== undefined &&
!/127\.0\.0\.1|localhost|::1/.test(ev.context.clientAddress)
) {
let found = log.findIndex((a) => a.ip === ev.context.clientAddress); let found = log.findIndex((a) => a.ip === ev.context.clientAddress);
if (found !== -1) { if (found !== -1) {
ev.context.currentUser.ipLog[found].lastAccess = new Date(); ev.context.currentUser.ipLog[found].lastAccess = new Date();

View File

@ -4,10 +4,7 @@ export default eventHandler(async (event) => {
let y = new Date().getFullYear(); let y = new Date().getFullYear();
let fmfilt: any = {}; let fmfilt: any = {};
if ( if (!!process.env.JulyFicmas && new Date() < new Date(Date.parse("Aug 1 " + y))) {
!!process.env.JulyFicmas &&
new Date() < new Date(Date.parse("Aug 1 " + y))
) {
fmfilt.isAnniversary = true; fmfilt.isAnniversary = true;
fmfilt.year = y; fmfilt.year = y;
} else if (new Date() < new Date(Date.parse("Dec 25 " + y))) { } else if (new Date() < new Date(Date.parse("Dec 25 " + y))) {

View File

@ -5,9 +5,7 @@ export default eventHandler(async (ev) => {
ev.node.res.on("close", () => { ev.node.res.on("close", () => {
p.done({ p.done({
label: "http/request", label: "http/request",
message: `{${ message: `{${ev.context.currentUser?.username || "guest"}} | ${ev.method.toLocaleUpperCase()} @ ${ev._path}`,
ev.context.currentUser?.username || "guest"
}} | ${ev.method.toLocaleUpperCase()} @ ${ev._path}`,
}); });
}); });
}); });

29
typings/auth.d.ts vendored
View File

@ -1,12 +1,5 @@
import { IUser } from "@models/user"; import { IUser } from "@models/user";
import { import { GetSessionFunc, SecondarySignInOptions, SessionLastRefreshedAt, SessionStatus, SignInFunc, SignOutFunc } from "@sidebase/nuxt-auth/dist/runtime/types";
GetSessionFunc,
SecondarySignInOptions,
SessionLastRefreshedAt,
SessionStatus,
SignInFunc,
SignOutFunc,
} from "@sidebase/nuxt-auth/dist/runtime/types";
import { ComputedRef, Ref } from "vue"; import { ComputedRef, Ref } from "vue";
declare module "#auth" { declare module "#auth" {
@ -24,18 +17,10 @@ declare module "@sidebase/nuxt-auth/dist/runtime/types" {
declare const signIn: SignInFunc<Credentials, any>; declare const signIn: SignInFunc<Credentials, any>;
declare const signOut: SignOutFunc; declare const signOut: SignOutFunc;
declare const getSession: GetSessionFunc<SessionData | null | void>; declare const getSession: GetSessionFunc<SessionData | null | void>;
declare const signUp: ( declare const signUp: (credentials: Credentials, signInOptions?: SecondarySignInOptions) => Promise<any>;
credentials: Credentials,
signInOptions?: SecondarySignInOptions,
) => Promise<any>;
type WrappedSessionData<SessionData> = Ref<SessionData | null | undefined>; type WrappedSessionData<SessionData> = Ref<SessionData | null | undefined>;
export interface CommonUseAuthReturn< export interface CommonUseAuthReturn<SignIn, SignOut, GetSession, SessionData> {
SignIn,
SignOut,
GetSession,
SessionData,
> {
data: Readonly<WrappedSessionData<SessionData>>; data: Readonly<WrappedSessionData<SessionData>>;
lastRefreshedAt: Readonly<Ref<SessionLastRefreshedAt>>; lastRefreshedAt: Readonly<Ref<SessionLastRefreshedAt>>;
status: ComputedRef<SessionStatus>; status: ComputedRef<SessionStatus>;
@ -43,13 +28,7 @@ declare module "@sidebase/nuxt-auth/dist/runtime/types" {
signOut: SignOut; signOut: SignOut;
getSession: GetSession; getSession: GetSession;
} }
interface UseAuthReturn interface UseAuthReturn extends CommonUseAuthReturn<typeof signIn, typeof signOut, typeof getSession, SessionData> {
extends CommonUseAuthReturn<
typeof signIn,
typeof signOut,
typeof getSession,
SessionData
> {
signUp: typeof signUp; signUp: typeof signUp;
token: Readonly<Ref<string | null>>; token: Readonly<Ref<string | null>>;
} }