style(*): edit indents
This commit is contained in:
parent
c8e84c909e
commit
8f45dbe563
@ -1,6 +1,7 @@
|
||||
[*.*]
|
||||
root = true
|
||||
[*]
|
||||
tab_width = 2
|
||||
indent_size = 1
|
||||
indent_size = 2
|
||||
indent_style = tab
|
||||
charset = utf-8
|
||||
max_line_length = 160
|
@ -2,25 +2,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>
|
||||
© 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 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 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 />
|
||||
© 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
|
||||
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
|
||||
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
|
||||
<a style="font-weight: bold" href="mailto:bugs@rockfic.com"
|
||||
>bugs@rockfic.com</a
|
||||
>.<br />
|
||||
<a style="font-weight: bold" href="mailto:bugs@rockfic.com">bugs@rockfic.com</a>.<br />
|
||||
For everything else, contact
|
||||
<a href="mailto:admin@rockfic.com">admin@rockfic.com</a>.
|
||||
</p>
|
||||
<b>Copyright Notice</b><br />
|
||||
All content on this site is copyright of its respective author. You may not,
|
||||
except with our express written permission, distribute or commercially
|
||||
exploit the content. Nor may you transmit it or store it in any other
|
||||
website or other form of electronic retrieval system.
|
||||
All content on this site is copyright of its respective author. You may not, except with our express written permission, distribute or commercially exploit
|
||||
the content. Nor may you transmit it or store it in any other website or other form of electronic retrieval system.
|
||||
</div>
|
||||
</template>
|
||||
|
@ -30,11 +30,7 @@
|
||||
promote: !short,
|
||||
},
|
||||
});
|
||||
messageApi.success(
|
||||
`User ${props.user?.username} is now ${
|
||||
short ? "an admin" : "a regular user"
|
||||
}.`,
|
||||
);
|
||||
messageApi.success(`User ${props.user?.username} is now ${short ? "an admin" : "a regular user"}.`);
|
||||
setTimeout(() => {
|
||||
showDemote.value = false;
|
||||
}, 1000);
|
||||
@ -44,26 +40,15 @@
|
||||
<template>
|
||||
<a-space :size="10" direction="vertical">
|
||||
<div>
|
||||
<a-descriptions
|
||||
:colon="false"
|
||||
:label-style="{ fontWeight: 'bold' }"
|
||||
:column="1"
|
||||
>
|
||||
<a-descriptions :colon="false" :label-style="{ fontWeight: 'bold' }" :column="1">
|
||||
<a-descriptions-item label="IP addresses">
|
||||
<a-list :data-source="user?.ipLog">
|
||||
<template #renderItem="{ item }">
|
||||
{{ item.ip }}<br />
|
||||
<a-typography-title :level="5"
|
||||
>Other users with this IP:</a-typography-title
|
||||
>
|
||||
<a-typography-title :level="5">Other users with this IP:</a-typography-title>
|
||||
<div v-if="commonIps != null">
|
||||
<i v-if="!commonIps[item.ip]?.length">
|
||||
No other users share this IP.
|
||||
</i>
|
||||
<a-list
|
||||
v-else
|
||||
:data-source="!!commonIps ? commonIps[item.ip] : []"
|
||||
>
|
||||
<i v-if="!commonIps[item.ip]?.length"> No other users share this IP. </i>
|
||||
<a-list v-else :data-source="!!commonIps ? commonIps[item.ip] : []">
|
||||
<template #renderItem="{ item: otherItem }">
|
||||
<nuxt-link :to="`/user/${otherItem._id}`">
|
||||
{{ otherItem.username }}
|
||||
@ -83,16 +68,10 @@
|
||||
<b>{{ user?.profile.isAdmin ? "an admin" : "a regular user" }}</b
|
||||
>.
|
||||
</span>
|
||||
<a-button
|
||||
danger
|
||||
v-if="!user?.profile.isAdmin"
|
||||
@click="() => (showDemote = true)"
|
||||
>
|
||||
<a-button danger v-if="!user?.profile.isAdmin" @click="() => (showDemote = true)">
|
||||
<b>Promote to Admin</b>
|
||||
</a-button>
|
||||
<a-button v-else @click="() => (showDemote = true)">
|
||||
Demote to regular user
|
||||
</a-button>
|
||||
<a-button v-else @click="() => (showDemote = true)"> Demote to regular user </a-button>
|
||||
</a-space>
|
||||
<a-divider />
|
||||
<div style="display: flex">
|
||||
@ -110,8 +89,7 @@
|
||||
v-model:open="showBanUnban"
|
||||
:title="`${user?.banned ? 'Unban' : 'Ban'} ${user?.username}`"
|
||||
>
|
||||
Are you sure you want to {{ `${user?.banned ? "unban" : "ban"}` }}
|
||||
{{ user?.username }}?
|
||||
Are you sure you want to {{ `${user?.banned ? "unban" : "ban"}` }} {{ user?.username }}?
|
||||
</a-modal>
|
||||
<a-modal
|
||||
cancel-text="No"
|
||||
@ -119,20 +97,13 @@
|
||||
@ok="prodem"
|
||||
@cancel="() => (showDemote = false)"
|
||||
v-model:open="showDemote"
|
||||
:title="`${short ? 'Demoting' : 'Promoting'} ${user?.username} ${
|
||||
!short ? 'to an administrator' : 'to a regular user'
|
||||
}`"
|
||||
:title="`${short ? 'Demoting' : 'Promoting'} ${user?.username} ${!short ? 'to an administrator' : 'to a regular user'}`"
|
||||
>
|
||||
<div v-if="!short">
|
||||
Are you <b><u>absolutely sure</u></b> you want to
|
||||
<b>promote this user to an admin</b>?
|
||||
Are you <b><u>absolutely sure</u></b> you want to <b>promote this user to an admin</b>?
|
||||
<br />
|
||||
<a-typography-title :level="5">
|
||||
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?
|
||||
<a-typography-title :level="5"> 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>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
@ -4,9 +4,7 @@
|
||||
import { SingleChapterResult } from "@client/types/slightlyDifferentStory";
|
||||
const props = defineProps<{ endpoint: string }>();
|
||||
const story = inject<SingleChapterResult>("story");
|
||||
const { data: reviews } = (await useApiFetch<IReview[]>(
|
||||
`${props.endpoint}/reviews`,
|
||||
)) as unknown as {
|
||||
const { data: reviews } = (await useApiFetch<IReview[]>(`${props.endpoint}/reviews`)) as unknown as {
|
||||
data: IReview[];
|
||||
};
|
||||
</script>
|
||||
|
@ -12,20 +12,12 @@
|
||||
});
|
||||
return unflattened.flat(Infinity).map((a) => ({ value: a, label: a }));
|
||||
});
|
||||
const charField = useField<string[]>(
|
||||
fname + "characters",
|
||||
cs.fields.characters as unknown as MaybeRef<RuleExpression<string[]>>,
|
||||
);
|
||||
const charField = useField<string[]>(fname + "characters", cs.fields.characters as unknown as MaybeRef<RuleExpression<string[]>>);
|
||||
const { value, errorMessage, name: bandName, setValue } = charField;
|
||||
// setValue([]);
|
||||
</script>
|
||||
<template>
|
||||
<a-form-item
|
||||
:help="errorMessage"
|
||||
label="Characters"
|
||||
:name="bandName as string"
|
||||
:validate-status="!!errorMessage ? 'error' : undefined"
|
||||
>
|
||||
<a-form-item :help="errorMessage" label="Characters" :name="bandName as string" :validate-status="!!errorMessage ? 'error' : undefined">
|
||||
<a-select mode="multiple" :options="opts" v-model:value="value">
|
||||
<template #removeIcon>
|
||||
<i class="far fa-circle-x" />
|
||||
|
@ -6,22 +6,11 @@
|
||||
value: a,
|
||||
label: a,
|
||||
}));
|
||||
const { value, errorMessage, name, setValue } = useField<string[]>(
|
||||
fname + "genre",
|
||||
);
|
||||
const { value, errorMessage, name, setValue } = useField<string[]>(fname + "genre");
|
||||
</script>
|
||||
<template>
|
||||
<a-form-item
|
||||
:help="errorMessage"
|
||||
label="Genre(s)"
|
||||
:validate-status="!!errorMessage ? 'error' : undefined"
|
||||
>
|
||||
<a-select
|
||||
:allow-clear="true"
|
||||
:options="opts"
|
||||
v-model:value="value"
|
||||
mode="multiple"
|
||||
>
|
||||
<a-form-item :help="errorMessage" label="Genre(s)" :validate-status="!!errorMessage ? 'error' : undefined">
|
||||
<a-select :allow-clear="true" :options="opts" v-model:value="value" mode="multiple">
|
||||
<template #removeIcon>
|
||||
<i class="far fa-circle-x" />
|
||||
</template>
|
||||
|
@ -12,26 +12,14 @@
|
||||
});
|
||||
return uf.flat(Infinity).map((a) => ({ value: a, label: a }));
|
||||
});
|
||||
const { fields, push, remove, replace, update } = useFieldArray<string[]>(
|
||||
fname + "relationships",
|
||||
);
|
||||
const { fields, push, remove, replace, update } = useFieldArray<string[]>(fname + "relationships");
|
||||
// replace([]);
|
||||
</script>
|
||||
<template>
|
||||
<a-form-item label="Pairings">
|
||||
<a-row
|
||||
:gutter="5"
|
||||
:wrap="true"
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
>
|
||||
<a-row :gutter="5" :wrap="true" v-for="(field, idx) in fields" :key="field.key">
|
||||
<Field :name="fname + 'relationships' + `[${idx}]`">
|
||||
<a-select
|
||||
mode="multiple"
|
||||
:options="opts"
|
||||
v-model:value="field.value as string[]"
|
||||
@change="(val) => update(idx, val as string[])"
|
||||
>
|
||||
<a-select mode="multiple" :options="opts" v-model:value="field.value as string[]" @change="(val) => update(idx, val as string[])">
|
||||
<template #removeIcon>
|
||||
<i class="far fa-circle-x" />
|
||||
</template>
|
||||
|
@ -3,9 +3,7 @@
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
placeholder="Please select"
|
||||
:options="
|
||||
[...Array(25)].map((_, i) => ({ value: (i + 10).toString(36) + (i + 1) }))
|
||||
"
|
||||
:options="[...Array(25)].map((_, i) => ({ value: (i + 10).toString(36) + (i + 1) }))"
|
||||
@change="handleChange"
|
||||
></a-select>
|
||||
</template>
|
||||
|
@ -8,77 +8,45 @@
|
||||
</script>
|
||||
<template>
|
||||
<a-card style="width: 45%; float: left; margin-right: 1.2em" v-if="!!story">
|
||||
<a-descriptions
|
||||
:label-style="{ fontWeight: 'bold' }"
|
||||
:colon="false"
|
||||
:column="1"
|
||||
>
|
||||
<a-descriptions :label-style="{ fontWeight: 'bold' }" :colon="false" :column="1">
|
||||
<a-descriptions-item label="Author">
|
||||
<nuxt-link :to="`/user/${story?.author._id}`">{{
|
||||
story?.author.username
|
||||
}}</nuxt-link>
|
||||
<nuxt-link :to="`/user/${story?.author._id}`">{{ story?.author.username }}</nuxt-link>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Bands">
|
||||
<div
|
||||
class="wrapLong"
|
||||
v-for="(item, index) in story?.currentChapter.bands"
|
||||
>
|
||||
<div class="wrapLong" v-for="(item, index) in story?.currentChapter.bands">
|
||||
<span>
|
||||
<nuxt-link :to="`/band/${item._id}`">
|
||||
{{ item.name }}
|
||||
</nuxt-link>
|
||||
{{
|
||||
(index < story!.currentChapter?.bands.length - 1 && ", ") ||
|
||||
""
|
||||
}}
|
||||
{{ (index < story!.currentChapter?.bands.length - 1 && ", ") || "" }}
|
||||
</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Genre(s)">
|
||||
<div
|
||||
class="wrapLong"
|
||||
v-for="(item, index) in story?.currentChapter.genre"
|
||||
>
|
||||
<div class="wrapLong" v-for="(item, index) in story?.currentChapter.genre">
|
||||
<span>
|
||||
{{ item }}
|
||||
{{
|
||||
(index < story!.currentChapter?.genre.length - 1 && ", ") ||
|
||||
""
|
||||
}}
|
||||
{{ (index < story!.currentChapter?.genre.length - 1 && ", ") || "" }}
|
||||
</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Relationship(s)">
|
||||
<div
|
||||
class="wrapLong"
|
||||
v-for="(item, index) in story?.currentChapter.relationships"
|
||||
>
|
||||
<div class="wrapLong" v-for="(item, index) in story?.currentChapter.relationships">
|
||||
<span>
|
||||
{{ item.join("/") }}
|
||||
{{
|
||||
(index < story!.currentChapter?.relationships.length - 1 &&
|
||||
", ") ||
|
||||
""
|
||||
}}
|
||||
{{ (index < story!.currentChapter?.relationships.length - 1 && ", ") || "" }}
|
||||
</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Character(s)">
|
||||
<div class="wrapLong">
|
||||
<span v-for="(item, index) in story?.currentChapter.characters">
|
||||
{{ item
|
||||
}}{{
|
||||
(index < story!.currentChapter?.characters.length - 1 &&
|
||||
", ") ||
|
||||
""
|
||||
}}
|
||||
{{ item }}{{ (index < story!.currentChapter?.characters.length - 1 && ", ") || "" }}
|
||||
</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<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 label="Summary">
|
||||
<div v-html="story?.currentChapter.summary"></div>
|
||||
@ -86,19 +54,9 @@
|
||||
<a-descriptions-item label="Date posted">
|
||||
<a-tooltip>
|
||||
<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>
|
||||
{{
|
||||
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-descriptions-item>
|
||||
</a-descriptions>
|
||||
@ -106,12 +64,7 @@
|
||||
<div class="stats">
|
||||
<span>
|
||||
<span class="staticon">
|
||||
<icon
|
||||
:istyle="!dark ? 'solid' : 'regular'"
|
||||
icolor="#ff2883"
|
||||
:size="12"
|
||||
name="heart"
|
||||
/>
|
||||
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#ff2883" :size="12" name="heart" />
|
||||
</span>
|
||||
<span>
|
||||
{{ story.favs }}
|
||||
@ -119,12 +72,7 @@
|
||||
</span>
|
||||
<span>
|
||||
<span class="staticon">
|
||||
<icon
|
||||
:istyle="!dark ? 'solid' : 'regular'"
|
||||
icolor="#1787d7"
|
||||
:size="12"
|
||||
name="book-open"
|
||||
/>
|
||||
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#1787d7" :size="12" name="book-open" />
|
||||
</span>
|
||||
<span>
|
||||
{{ story.views }}
|
||||
@ -132,12 +80,7 @@
|
||||
</span>
|
||||
<span>
|
||||
<span class="staticon">
|
||||
<icon
|
||||
:istyle="!dark ? 'solid' : 'regular'"
|
||||
icolor="#51e07c"
|
||||
:size="12"
|
||||
name="thumbs-up"
|
||||
/>
|
||||
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#51e07c" :size="12" name="thumbs-up" />
|
||||
</span>
|
||||
<span>
|
||||
{{ story.recs }}
|
||||
@ -145,12 +88,7 @@
|
||||
</span>
|
||||
<span>
|
||||
<span class="staticon">
|
||||
<icon
|
||||
:istyle="!dark ? 'solid' : 'regular'"
|
||||
icolor="#c2d420"
|
||||
:size="12"
|
||||
name="download"
|
||||
/>
|
||||
<icon :istyle="!dark ? 'solid' : 'regular'" icolor="#c2d420" :size="12" name="download" />
|
||||
</span>
|
||||
<span>
|
||||
{{ story.downloads }}
|
||||
|
@ -2,95 +2,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>Age Policy</h3>
|
||||
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.
|
||||
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.
|
||||
<h3>General</h3>
|
||||
<ol>
|
||||
<li>Rockfic.com is not affiliated with any band listed on the site;</li>
|
||||
<li>
|
||||
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 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 persons named;
|
||||
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
|
||||
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
|
||||
persons named;
|
||||
</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>
|
||||
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>
|
||||
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.
|
||||
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>
|
||||
</ol>
|
||||
|
||||
<h3>Content</h3>
|
||||
<ol>
|
||||
<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>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 responsible for managing any content they create; this includes managing the privacy settings facilitated by the site;</li>
|
||||
<li>
|
||||
Authors are responsible for managing any content they create; this
|
||||
includes managing the privacy settings facilitated by the site;
|
||||
Rockfic.com reserves the right to remove content, including anything we consider offensive, inappropriate or defamatory, at our discretion; any 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>
|
||||
Rockfic.com reserves the right to remove content, including anything we
|
||||
consider offensive, inappropriate or defamatory, at our discretion; any
|
||||
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.
|
||||
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>
|
||||
</ol>
|
||||
|
||||
<h3>Conduct</h3>
|
||||
<ol>
|
||||
<li>
|
||||
Rockfic.com operates a strict anti-bullying and anti-harassment policy;
|
||||
if you have an issue to report, contact us;
|
||||
</li>
|
||||
<li>Rockfic.com operates a strict anti-bullying and anti-harassment policy; if you have an issue to report, contact us;</li>
|
||||
<li>
|
||||
Rockfic.com will permanently ban users who:
|
||||
<ol style="list-style-type: lower-alpha">
|
||||
<li>
|
||||
break the law, for example by saying something libellous, or by
|
||||
posting something which results in a criminal offence;
|
||||
</li>
|
||||
<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>impersonate another user;</li>
|
||||
<li>
|
||||
collect or use any information from Rockfic.com with the intent to
|
||||
harm, discredit or harass any other user; or
|
||||
</li>
|
||||
<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>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
<h3>Copyright</h3>
|
||||
All content on this site is copyright of its respective author. You may not,
|
||||
except with our express written permission, distribute or commercially
|
||||
exploit the content. Nor may you transmit it or store it in any other
|
||||
website or other form of electronic retrieval system.
|
||||
All content on this site is copyright of its respective author. You may not, except with our express written permission, distribute or commercially exploit
|
||||
the content. Nor may you transmit it or store it in any other website or other form of electronic retrieval system.
|
||||
</div>
|
||||
</template>
|
||||
|
@ -60,8 +60,7 @@ export const fancy = {
|
||||
items: "h1 h2 h3 h4 h5 h6",
|
||||
},
|
||||
},
|
||||
toolbar:
|
||||
"undo redo | paste | bold italic underline | hr image link | forecolor styles | heading alignment | code",
|
||||
toolbar: "undo redo | paste | bold italic underline | hr image link | forecolor styles | heading alignment | code",
|
||||
contextmenu: "bold italic underline | hr | link | image | paste",
|
||||
external_plugins: {
|
||||
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(
|
||||
" ",
|
||||
),
|
||||
toolbar:
|
||||
"undo redo | paste |" +
|
||||
"bold italic underline | hr | alignleft aligncenter " +
|
||||
"alignright alignjustify | " +
|
||||
"| code",
|
||||
toolbar: "undo redo | paste |" + "bold italic underline | hr | alignleft aligncenter " + "alignright alignjustify | " + "| code",
|
||||
contextmenu: "bold italic underline | hr | paste | link",
|
||||
};
|
||||
export const bare = {
|
||||
|
@ -3,12 +3,7 @@ import { FavPayload, HidePayload, SubPayload } from "./types/form/favSub";
|
||||
|
||||
const base = `/user/me`;
|
||||
|
||||
export const favourites = (
|
||||
values: (any & { _id: number })[],
|
||||
id: number,
|
||||
remove: boolean,
|
||||
type: "story" | "author",
|
||||
) => {
|
||||
export const favourites = (values: (any & { _id: number })[], id: number, remove: boolean, type: "story" | "author") => {
|
||||
values?.splice(
|
||||
values!.findIndex((a) => a._id == id),
|
||||
1,
|
||||
@ -26,12 +21,7 @@ export const favourites = (
|
||||
});
|
||||
};
|
||||
|
||||
export const subscriptions = (
|
||||
values: (any & { _id: number })[],
|
||||
id: number,
|
||||
action: "hide" | "subscribe" | "unsubscribe",
|
||||
type: "bands" | "authors",
|
||||
) => {
|
||||
export const subscriptions = (values: (any & { _id: number })[], id: number, action: "hide" | "subscribe" | "unsubscribe", type: "bands" | "authors") => {
|
||||
values?.splice(
|
||||
values!.findIndex((a) => a._id == id),
|
||||
1,
|
||||
|
@ -3,15 +3,16 @@ import { IChapter } from "@models/stories/chapter";
|
||||
import { IStory } from "@models/stories";
|
||||
import { messages } from "@server/constants";
|
||||
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) => {
|
||||
const { getSession } = useAuth();
|
||||
await getSession({ force: true });
|
||||
const { data } = useAuth();
|
||||
console.log("to n from", to, from, data);
|
||||
const { data: story, error } = await useApiFetch<SingleChapterResult>(
|
||||
to.path,
|
||||
);
|
||||
const { data: story, error } = await useApiFetch<SingleChapterResult>(to.path);
|
||||
if (error.value) {
|
||||
return showError(error.value);
|
||||
} else if (!story.value) {
|
||||
@ -23,24 +24,27 @@ export const storyMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
|
||||
}
|
||||
});
|
||||
|
||||
export const storyEditMiddleware = defineNuxtRouteMiddleware(
|
||||
async (to, from) => {
|
||||
const { data: curU } = useAuth();
|
||||
const rtr = useRoute();
|
||||
const { data: storyInfo } = await useApiFetch<
|
||||
({ chapters: (IChapter & { text: string })[] } & IStory) | null
|
||||
>(`/story/${rtr.params.id}/full`);
|
||||
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({
|
||||
statusCode: 403,
|
||||
message: messages[403],
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
export const storyEditMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
|
||||
const { data: curU } = useAuth();
|
||||
const rtr = useRoute();
|
||||
const { data: storyInfo } = await useApiFetch<({ chapters: (IChapter & { text: string })[] } & IStory) | null>(`/story/${rtr.params.id}/full`);
|
||||
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],
|
||||
});
|
||||
}
|
||||
});
|
||||
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],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
3
lib/client/types/form/draftInfo.ts
Normal file
3
lib/client/types/form/draftInfo.ts
Normal file
@ -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 {
|
||||
id?: number;
|
||||
@ -51,3 +53,21 @@ export const defaultStory: FormStory = {
|
||||
challenge: null,
|
||||
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 = (
|
||||
values: any,
|
||||
endpoint: string,
|
||||
method: "put" | "post",
|
||||
) => {
|
||||
export const autoEdit = (values: any, endpoint: string, method: "put" | "post") => {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
useApiFetch<{ success: boolean; data: IStory }>(endpoint, {
|
||||
method,
|
||||
|
@ -1,10 +1,8 @@
|
||||
import turndown from "turndown";
|
||||
export const ContentFilenameRegex = /\.(doc|docx|md|markdown)$/i;
|
||||
|
||||
export const emailRegex: RegExp =
|
||||
/^[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 emailRegex: RegExp = /^[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 mammothTemplate = (doc, defaults, content) => {
|
||||
return content.replace(/\n|\r\n|\r/gm, "");
|
||||
};
|
||||
@ -90,17 +88,7 @@ export const sanitizeConf = {
|
||||
img: ["src"],
|
||||
},
|
||||
// Lots of these won't come up by default because we don't allow them
|
||||
selfClosing: [
|
||||
"img",
|
||||
"br",
|
||||
"hr",
|
||||
"area",
|
||||
"base",
|
||||
"basefont",
|
||||
"input",
|
||||
"link",
|
||||
"meta",
|
||||
],
|
||||
selfClosing: ["img", "br", "hr", "area", "base", "basefont", "input", "link", "meta"],
|
||||
// URL schemes we permit
|
||||
allowedSchemes: ["http", "https", "ftp", "mailto", "tel"],
|
||||
allowedSchemesAppliedToAttributes: ["href", "src", "cite"],
|
||||
|
20
lib/server/dbHelpers/draftHydrator.ts
Normal file
20
lib/server/dbHelpers/draftHydrator.ts
Normal 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;
|
||||
}
|
@ -20,7 +20,6 @@ export default async function (ev: H3Event<EventHandlerRequest>) {
|
||||
})
|
||||
.populate({ path: "challenge", model: Challenge })
|
||||
.exec();
|
||||
if (story == null)
|
||||
throw createError({ statusCode: 404, message: "Not found." });
|
||||
if (story == null) throw createError({ statusCode: 404, message: "Not found." });
|
||||
return story;
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
export const submissionsOpen = () =>
|
||||
new Date() < new Date(Date.parse(`Dec 24 ${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 status = () =>
|
||||
Date.now() > Date.parse(`Oct 1 ${new Date().getFullYear()}`) &&
|
||||
Date.now() < Date.parse(`Nov 30 ${new Date().getFullYear()}`);
|
||||
new Date() < new Date(Date.parse(`Dec 24 ${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 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";
|
||||
export function canDelete(event: H3Event<EventHandlerRequest>, story: IStory) {
|
||||
isLoggedIn(event);
|
||||
return (
|
||||
event.context.currentUser?.profile.isAdmin ||
|
||||
(story.author as IUser)._id === event.context.currentUser?._id
|
||||
);
|
||||
return event.context.currentUser?.profile.isAdmin || (story.author as IUser)._id === event.context.currentUser?._id;
|
||||
}
|
||||
export function canDeleteDraft(
|
||||
event: H3Event<EventHandlerRequest>,
|
||||
story: IDraft,
|
||||
) {
|
||||
export function canDeleteDraft(event: H3Event<EventHandlerRequest>, story: IDraft) {
|
||||
isLoggedIn(event);
|
||||
return story.author === event.context.currentUser?._id;
|
||||
}
|
||||
export function canModify(
|
||||
event: H3Event<EventHandlerRequest>,
|
||||
story: IStory | IDraft,
|
||||
) {
|
||||
export function canModify(event: H3Event<EventHandlerRequest>, story: IStory | IDraft) {
|
||||
isLoggedIn(event);
|
||||
return (
|
||||
event.context.currentUser?._id === (story.author as IUser)._id ||
|
||||
(story.coAuthor as IUser)?._id === event.context.currentUser?._id
|
||||
);
|
||||
return 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;
|
||||
} else if (bodyObj.file) {
|
||||
let ext = extname(bodyObj.file).toLowerCase();
|
||||
if (ext === ".md" || ext === ".markdown")
|
||||
str = marked.parse(
|
||||
readFileSync(resolve(`tmp/${bodyObj.file}`)).toString(),
|
||||
);
|
||||
if (ext === ".md" || ext === ".markdown") str = marked.parse(readFileSync(resolve(`tmp/${bodyObj.file}`)).toString());
|
||||
else if (ext === ".doc" || ext === ".docx")
|
||||
str = (
|
||||
await mammoth.convertToHtml(
|
||||
{ path: resolve(`tmp/${bodyObj.file}`) },
|
||||
{ styleMap: ["b => b", "i => i", "u => u"] },
|
||||
)
|
||||
).value;
|
||||
str = (await mammoth.convertToHtml({ path: resolve(`tmp/${bodyObj.file}`) }, { styleMap: ["b => b", "i => i", "u => u"] })).value;
|
||||
else
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
|
@ -1,9 +1,6 @@
|
||||
import getBucket from "./getBucket";
|
||||
import { Readable } from "stream";
|
||||
export default async function replaceGridFS(
|
||||
chapterID: number | undefined,
|
||||
content: string,
|
||||
) {
|
||||
export default async function replaceGridFS(chapterID: number | undefined, content: string) {
|
||||
let filename = `/stories/${chapterID}.txt`;
|
||||
const bucket = getBucket();
|
||||
if (chapterID) {
|
||||
|
@ -4,9 +4,7 @@
|
||||
import { subscriptions, bp } from "@client/listActions";
|
||||
import { IUser } from "@models/user";
|
||||
|
||||
const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>(
|
||||
"/band/all",
|
||||
)) as unknown as { data: Ref<IBand[]> };
|
||||
const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>("/band/all")) as unknown as { data: Ref<IBand[]> };
|
||||
|
||||
const { data: rd }: { data: any } = useAuth();
|
||||
const data = rd as { user: IUser };
|
||||
@ -29,16 +27,10 @@
|
||||
</a-col>
|
||||
<!-- subscribe... -->
|
||||
<a-col v-if="data && data.user?._id" style="margin-left: auto">
|
||||
<a
|
||||
v-if="!data?.user.subscriptions.bands.includes(item._id)"
|
||||
@click="(e) => hider(bands, item._id, 'subscribe', 'bands')"
|
||||
>
|
||||
<a 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" />
|
||||
</a>
|
||||
<a
|
||||
v-else
|
||||
@click="(e) => hider(bands, item._id, 'unsubscribe', 'bands')"
|
||||
>
|
||||
<a v-else @click="(e) => hider(bands, item._id, 'unsubscribe', 'bands')">
|
||||
<icon :istyle="'regular'" name="x" :size="12" />
|
||||
</a>
|
||||
</a-col>
|
||||
|
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 90vh">
|
||||
<iframe
|
||||
style="width: 100%; height: 100%"
|
||||
src="https://www.rockfic.com/forum/"
|
||||
/>
|
||||
<iframe style="width: 100%; height: 100%" src="https://www.rockfic.com/forum/" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -10,10 +10,5 @@
|
||||
</script>
|
||||
<template>
|
||||
<a-typography-title> Post a new Story </a-typography-title>
|
||||
<story-form
|
||||
endpoint-method="post"
|
||||
:can-draft="true"
|
||||
:data="defaultStory"
|
||||
endpoint="/story/new"
|
||||
/>
|
||||
<story-form endpoint-method="post" :can-draft="true" :data="defaultStory" endpoint="/story/new" />
|
||||
</template>
|
||||
|
@ -12,9 +12,7 @@
|
||||
middleware: [storyMiddleware],
|
||||
});
|
||||
const rtr = useRoute();
|
||||
const { data: story, error } = await useApiFetch<SingleChapterResult>(
|
||||
`/story/${rtr.params.id}/${rtr.params.cidx}`,
|
||||
);
|
||||
const { data: story, error } = await useApiFetch<SingleChapterResult>(`/story/${rtr.params.id}/${rtr.params.cidx}`);
|
||||
provide<SingleChapterResult | null>("story", story.value);
|
||||
console.log("storyyy", story.value?.currentChapter);
|
||||
console.log(rtr);
|
||||
@ -52,55 +50,24 @@
|
||||
<div v-html="story?.currentChapter.text"></div>
|
||||
<a-divider style="background-color: #fff" />
|
||||
<a-button-group size="large" v-if="story.totalChapters > 1">
|
||||
<a-button
|
||||
v-if="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
|
||||
}`,
|
||||
)
|
||||
"
|
||||
>
|
||||
<a-button v-if="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
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="
|
||||
parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1
|
||||
"
|
||||
@click="
|
||||
() =>
|
||||
navigateTo(
|
||||
`/story/${rtr.params.id}/${
|
||||
parseInt(rtr.params.cidx as string) + 1
|
||||
}`,
|
||||
)
|
||||
"
|
||||
v-if="parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1"
|
||||
@click="() => navigateTo(`/story/${rtr.params.id}/${parseInt(rtr.params.cidx as string) + 1}`)"
|
||||
>
|
||||
Next
|
||||
</a-button>
|
||||
<a-button
|
||||
@click="
|
||||
() =>
|
||||
navigateTo(`/story/${rtr.params.id}/${story.chapterNames.length}`)
|
||||
"
|
||||
v-if="
|
||||
parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1
|
||||
"
|
||||
@click="() => navigateTo(`/story/${rtr.params.id}/${story.chapterNames.length}`)"
|
||||
v-if="parseInt(rtr.params.cidx as string) < story.chapterNames.length - 1"
|
||||
>
|
||||
Last
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
<a-typography-title style="text-align: center" :level="2">
|
||||
Reviews
|
||||
</a-typography-title>
|
||||
<a-typography-title style="text-align: center" :level="2"> Reviews </a-typography-title>
|
||||
<for-chapter :endpoint="`/story/${rtr.params.id}/${rtr.params.cidx}`" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -6,9 +6,7 @@
|
||||
middleware: [storyMiddleware],
|
||||
});
|
||||
const rtr = useRoute();
|
||||
const { data: story, error } = await useApiFetch<IStory>(
|
||||
`/story/${rtr.params.id}`,
|
||||
);
|
||||
const { data: story, error } = await useApiFetch<IStory>(`/story/${rtr.params.id}`);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -15,10 +15,7 @@ export default eventHandler(async (event) => {
|
||||
await captcha(event);
|
||||
console.log("fields exist");
|
||||
const user = await User.findOne({
|
||||
$or: [
|
||||
{ username: usernameRegex(body.username) },
|
||||
{ email: (body.email as string).toLowerCase() },
|
||||
],
|
||||
$or: [{ username: usernameRegex(body.username) }, { email: (body.email as string).toLowerCase() }],
|
||||
});
|
||||
console.log("after f0", user);
|
||||
if (user)
|
||||
@ -27,7 +24,7 @@ export default eventHandler(async (event) => {
|
||||
message: "A user with that username or email already exists.",
|
||||
});
|
||||
let nuser = new User({
|
||||
email: body.email.toLowerCase(),
|
||||
email: body.email.toLowerCase().trim(),
|
||||
username: w2nc(body.username.trim()),
|
||||
password: User.generateHash(body.password),
|
||||
auth: {
|
||||
|
@ -14,10 +14,7 @@ let authorSingleton: {
|
||||
const threshold = 60 * 60 * 1000;
|
||||
export default cachedEventHandler(
|
||||
async (ev) => {
|
||||
if (
|
||||
Date.now() - authorSingleton.lastRefreshed >= threshold ||
|
||||
authorSingleton.data.length < 1
|
||||
)
|
||||
if (Date.now() - authorSingleton.lastRefreshed >= threshold || authorSingleton.data.length < 1)
|
||||
authorSingleton.data = await User.aggregate([
|
||||
{ $project: { username: true, _id: true } },
|
||||
{
|
||||
@ -38,10 +35,8 @@ export default cachedEventHandler(
|
||||
},
|
||||
]);
|
||||
authorSingleton.data.sort((a, b) => {
|
||||
if (a.username.toLocaleUpperCase() > b.username.toLocaleUpperCase())
|
||||
return 1;
|
||||
else if (a.username.toLocaleUpperCase() < b.username.toLocaleUpperCase())
|
||||
return -1;
|
||||
if (a.username.toLocaleUpperCase() > b.username.toLocaleUpperCase()) return 1;
|
||||
else if (a.username.toLocaleUpperCase() < b.username.toLocaleUpperCase()) return -1;
|
||||
return 0;
|
||||
});
|
||||
return authorSingleton.data;
|
||||
|
@ -19,10 +19,7 @@ export default eventHandler(async (ev) => {
|
||||
statusCode: 400,
|
||||
message: "bad parameter",
|
||||
});
|
||||
if (
|
||||
ev.context.currentUser!._id != s2v?.author &&
|
||||
ev.context.currentUser!._id != c2d._id
|
||||
)
|
||||
if (ev.context.currentUser!._id != s2v?.author && ev.context.currentUser!._id != c2d._id)
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: messages[403],
|
||||
|
@ -4,9 +4,7 @@ import { isIdNan } from "@server/middlewareButNotReally";
|
||||
|
||||
export default eventHandler(async (ev) => {
|
||||
const revid = isIdNan(ev);
|
||||
const r = await Review.findById(revid)
|
||||
.populate("author", "username _id")
|
||||
.exec();
|
||||
const r = await Review.findById(revid).populate("author", "username _id").exec();
|
||||
if (!r) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
|
@ -32,8 +32,6 @@ export default eventHandler(async (ev) => {
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
data: await Review.findById(revid)
|
||||
.populate("author", "username profile _id")
|
||||
.exec(),
|
||||
data: await Review.findById(revid).populate("author", "username profile _id").exec(),
|
||||
};
|
||||
});
|
||||
|
@ -20,9 +20,7 @@ export default eventHandler(async (ev) => {
|
||||
});
|
||||
}
|
||||
if (
|
||||
(replyingTo?.author as IUser).blocked.includes(
|
||||
ev.context.currentUser!._id,
|
||||
) ||
|
||||
(replyingTo?.author as IUser).blocked.includes(ev.context.currentUser!._id) ||
|
||||
ev.context.currentUser!.blocked.includes((replyingTo?.author as IUser)._id)
|
||||
) {
|
||||
throw createError({
|
||||
@ -40,9 +38,7 @@ export default eventHandler(async (ev) => {
|
||||
datePosted: new Date(),
|
||||
});
|
||||
const { _id } = await newReply.save();
|
||||
const nrs = (await Review.findOne({ _id })
|
||||
.populate("author", "username _id blocked")
|
||||
.exec())!;
|
||||
const nrs = (await Review.findOne({ _id }).populate("author", "username _id blocked").exec())!;
|
||||
replyingTo.replies.push(nrs._id);
|
||||
await replyingTo.save();
|
||||
const story = await Story.findById(replyingTo.leftOn);
|
||||
@ -52,9 +48,7 @@ export default eventHandler(async (ev) => {
|
||||
});
|
||||
}
|
||||
return {
|
||||
back: `/story/${replyingTo.leftOn}/${
|
||||
story!.chapters.findIndex((x) => x.id === nrs.whichChapter) + 1
|
||||
}`,
|
||||
back: `/story/${replyingTo.leftOn}/${story!.chapters.findIndex((x) => x.id === nrs.whichChapter) + 1}`,
|
||||
data: nrs.toObject(),
|
||||
success: true,
|
||||
};
|
||||
|
@ -8,11 +8,7 @@ export default eventHandler(async (ev) => {
|
||||
isLoggedIn(ev);
|
||||
const s = await storyQuerier(ev);
|
||||
const hidden = s.chapters.some((a) => a.hidden);
|
||||
if (
|
||||
hidden &&
|
||||
ev.context.currentUser?._id !== (s.author as IUser)._id &&
|
||||
!ev.context.currentUser?.profile.isAdmin
|
||||
) {
|
||||
if (hidden && ev.context.currentUser?._id !== (s.author as IUser)._id && !ev.context.currentUser?.profile.isAdmin) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: messages[403],
|
||||
|
@ -20,10 +20,7 @@ export default cachedEventHandler(
|
||||
async (event) => {
|
||||
let aa = mongoose.connection.db.collection("z_index_totAuthors");
|
||||
let totalStories = await Story.countDocuments({ "chapters.hidden": false });
|
||||
if (
|
||||
!authorSingleton.data.length ||
|
||||
Date.now() - authorSingleton.lastRefreshed >= threshold
|
||||
) {
|
||||
if (!authorSingleton.data.length || Date.now() - authorSingleton.lastRefreshed >= threshold) {
|
||||
authorSingleton.data = await User.aggregate([
|
||||
{ $project: { username: true, _id: true } },
|
||||
{
|
||||
|
@ -16,9 +16,5 @@ export default eventHandler(async (ev) => {
|
||||
})
|
||||
.populate("story")
|
||||
.exec();
|
||||
return ar
|
||||
.map((a) => a.toObject())
|
||||
.sort(
|
||||
(a, b) => b.datePosted.getMilliseconds() - a.datePosted.getMilliseconds(),
|
||||
);
|
||||
return ar.map((a) => a.toObject()).sort((a, b) => b.datePosted.getMilliseconds() - a.datePosted.getMilliseconds());
|
||||
});
|
||||
|
@ -1,10 +1,7 @@
|
||||
export default eventHandler(async (ev) => {
|
||||
if (ev.context.currentUser) {
|
||||
let log = ev.context.currentUser.ipLog;
|
||||
if (
|
||||
ev.context.clientAddress !== undefined &&
|
||||
!/127\.0\.0\.1|localhost|::1/.test(ev.context.clientAddress)
|
||||
) {
|
||||
if (ev.context.clientAddress !== undefined && !/127\.0\.0\.1|localhost|::1/.test(ev.context.clientAddress)) {
|
||||
let found = log.findIndex((a) => a.ip === ev.context.clientAddress);
|
||||
if (found !== -1) {
|
||||
ev.context.currentUser.ipLog[found].lastAccess = new Date();
|
||||
|
@ -4,10 +4,7 @@ export default eventHandler(async (event) => {
|
||||
let y = new Date().getFullYear();
|
||||
let fmfilt: any = {};
|
||||
|
||||
if (
|
||||
!!process.env.JulyFicmas &&
|
||||
new Date() < new Date(Date.parse("Aug 1 " + y))
|
||||
) {
|
||||
if (!!process.env.JulyFicmas && new Date() < new Date(Date.parse("Aug 1 " + y))) {
|
||||
fmfilt.isAnniversary = true;
|
||||
fmfilt.year = 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", () => {
|
||||
p.done({
|
||||
label: "http/request",
|
||||
message: `{${
|
||||
ev.context.currentUser?.username || "guest"
|
||||
}} | ${ev.method.toLocaleUpperCase()} @ ${ev._path}`,
|
||||
message: `{${ev.context.currentUser?.username || "guest"}} | ${ev.method.toLocaleUpperCase()} @ ${ev._path}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
29
typings/auth.d.ts
vendored
29
typings/auth.d.ts
vendored
@ -1,12 +1,5 @@
|
||||
import { IUser } from "@models/user";
|
||||
import {
|
||||
GetSessionFunc,
|
||||
SecondarySignInOptions,
|
||||
SessionLastRefreshedAt,
|
||||
SessionStatus,
|
||||
SignInFunc,
|
||||
SignOutFunc,
|
||||
} from "@sidebase/nuxt-auth/dist/runtime/types";
|
||||
import { GetSessionFunc, SecondarySignInOptions, SessionLastRefreshedAt, SessionStatus, SignInFunc, SignOutFunc } from "@sidebase/nuxt-auth/dist/runtime/types";
|
||||
import { ComputedRef, Ref } from "vue";
|
||||
|
||||
declare module "#auth" {
|
||||
@ -24,18 +17,10 @@ declare module "@sidebase/nuxt-auth/dist/runtime/types" {
|
||||
declare const signIn: SignInFunc<Credentials, any>;
|
||||
declare const signOut: SignOutFunc;
|
||||
declare const getSession: GetSessionFunc<SessionData | null | void>;
|
||||
declare const signUp: (
|
||||
credentials: Credentials,
|
||||
signInOptions?: SecondarySignInOptions,
|
||||
) => Promise<any>;
|
||||
declare const signUp: (credentials: Credentials, signInOptions?: SecondarySignInOptions) => Promise<any>;
|
||||
|
||||
type WrappedSessionData<SessionData> = Ref<SessionData | null | undefined>;
|
||||
export interface CommonUseAuthReturn<
|
||||
SignIn,
|
||||
SignOut,
|
||||
GetSession,
|
||||
SessionData,
|
||||
> {
|
||||
export interface CommonUseAuthReturn<SignIn, SignOut, GetSession, SessionData> {
|
||||
data: Readonly<WrappedSessionData<SessionData>>;
|
||||
lastRefreshedAt: Readonly<Ref<SessionLastRefreshedAt>>;
|
||||
status: ComputedRef<SessionStatus>;
|
||||
@ -43,13 +28,7 @@ declare module "@sidebase/nuxt-auth/dist/runtime/types" {
|
||||
signOut: SignOut;
|
||||
getSession: GetSession;
|
||||
}
|
||||
interface UseAuthReturn
|
||||
extends CommonUseAuthReturn<
|
||||
typeof signIn,
|
||||
typeof signOut,
|
||||
typeof getSession,
|
||||
SessionData
|
||||
> {
|
||||
interface UseAuthReturn extends CommonUseAuthReturn<typeof signIn, typeof signOut, typeof getSession, SessionData> {
|
||||
signUp: typeof signUp;
|
||||
token: Readonly<Ref<string | null>>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user