style(*): edit indents

This commit is contained in:
parent c8e84c909e
commit 8f45dbe563
Signed by: tablet
GPG Key ID: 924A5F6AF051E87C
41 changed files with 198 additions and 491 deletions

@ -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

@ -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>

@ -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>

@ -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>

@ -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" />

@ -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>

@ -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>

@ -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>

@ -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 }}

@ -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>

@ -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 = {

@ -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,

@ -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< const { data: storyInfo } = await useApiFetch<({ chapters: (IChapter & { text: string })[] } & IStory) | null>(`/story/${rtr.params.id}/full`);
({ chapters: (IChapter & { text: string })[] } & IStory) | null if (!storyInfo.value) show404();
>(`/story/${rtr.params.id}/full`); if (curU.value?.user?._id !== (storyInfo.value?.author as IUser)._id && curU.value?.user?._id !== (storyInfo.value?.coAuthor as IUser)?._id) {
if (!storyInfo.value) {
return showError({ statusCode: 404, message: messages[404] });
}
if (
curU.value?.user?._id !== (storyInfo.value?.author as IUser)._id &&
curU.value?.user?._id !== (storyInfo.value?.coAuthor as IUser)?._id
) {
return showError({ return showError({
statusCode: 403, statusCode: 403,
message: messages[403], message: messages[403],
}); });
} }
}, });
); export const draftEditMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
const { data: curU } = useAuth();
const rtr = useRoute();
const { data: storyInfo } = await useApiFetch<IDraft | null>(`/draft/${rtr.params.id}`);
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],
});
}
});

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

@ -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(),
};
}

@ -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,

@ -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"],

@ -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;
}

@ -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;
} }

@ -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()}`);

@ -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
);
} }

@ -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,

@ -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) {

@ -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>

@ -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>

@ -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>

@ -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>

@ -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>

@ -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: {

@ -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;

@ -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],

@ -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,

@ -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(),
}; };
}); });

@ -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,
}; };

@ -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],

@ -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 } },
{ {

@ -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(),
);
}); });

@ -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();

@ -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))) {

@ -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

@ -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>>;
} }