Compare commits

..

10 Commits

Author SHA1 Message Date
4fe1e34a3e
fix(nuxt): fix readability
fix global text readability in night mode by changing the color to white
2024-12-09 16:00:43 -05:00
0898a622eb
fix(typing): augment node types (kinda)
do some finagling to shut typescript up about "staging" being an invalid value for `process.env.NODE_ENV`
2024-12-09 15:55:05 -05:00
f447c0f88f
refactor(components): remove another bulky console.log 2024-12-09 15:46:50 -05:00
4b6771c0dd
refactor(components): update navbar
- unwrap add story button from div and nuxt-link
- use `onclick` handler to navigate to the "new story" page
- better align the new story button with the rest of the navbar on tablet+ viewports
2024-12-09 15:43:44 -05:00
718fcc6ff8
fix(components): update error boundary
ensure that styling is consistent by surrounding with `a-extract-style`
2024-12-09 15:38:16 -05:00
480655a0ee
refactor(server/middleware): update current user middleware
- check for titlecased version of auth header
- check for token's `sub` field as well as `id`
- ensure we don't select sensitive info when querying the user
- don't throw if there's no user logged in for that request
2024-12-09 15:34:30 -05:00
53409d12da
refactor(api): update session retrieval route
ensure we also check for the titlecased version of `authorization` header
2024-12-08 23:27:47 -05:00
26dfbda2c7
refactor(pages): remove bulky console.log from story page 2024-12-08 23:24:19 -05:00
e6ba48dc2f
refactor(client-side): update story check middleware
if fetching a story returns an error, check that the error code is 403 (forbidden) as well before redirecting
2024-12-08 23:22:40 -05:00
80dbd05468
refactor(pages): login
add loading indicator to button after submit
2024-12-08 22:54:40 -05:00
13 changed files with 116 additions and 75 deletions

View File

@ -46,4 +46,8 @@
body {
margin: 0 !important;
}
body[data-theme="dark"] {
color: #fff;
}
</style>

View File

@ -18,7 +18,8 @@ ${p.error.stack}
`;
</script>
<template>
<a-typography-title> We ran into an issue.... :( <br /> </a-typography-title>
<a-extract-style>
<a-typography-title> We ran into an issue.... :( <br /></a-typography-title>
<a-typography-paragraph
style="font-size: 125%"
:copyable="{
@ -35,9 +36,10 @@ ${p.error.stack}
<a-typography-text code>
{{ JSON.stringify(unError, null, "\t") }}
</a-typography-text>
<a-typography-title :level="4"> Stack trace: </a-typography-title>
<a-typography-title :level="4"> Stack trace:</a-typography-title>
<a-typography-text>
<a-typography-text v-html="error.stack"> </a-typography-text>
<a-typography-text v-html="error.stack"></a-typography-text>
</a-typography-text>
</a-typography-paragraph>
</a-extract-style>
</template>

View File

@ -1,5 +1,6 @@
<script lang="ts" setup>
import { Grid, ItemType } from "ant-design-vue";
import { Grid } from "ant-design-vue";
const bp = Grid.useBreakpoint();
const { data, status } = useAuth();
const itemMap = ref({
@ -46,35 +47,31 @@
:active-key="cur"
:key="data?.user?._id"
>
<a-menu-item key="home"> Home </a-menu-item>
<a-menu-item key="bands"> Bands </a-menu-item>
<a-menu-item key="authors"> Authors </a-menu-item>
<a-menu-item key="home"> Home</a-menu-item>
<a-menu-item key="bands"> Bands</a-menu-item>
<a-menu-item key="authors"> Authors</a-menu-item>
<a-menu-item key="forum"> Message Board</a-menu-item>
<a-sub-menu :disabled="!data?.user" v-if="!!data?.user" title="My Stuff" key="group/my-stuff">
<a-menu-item key="account"> Account </a-menu-item>
<a-menu-item key="edit-profile"> Edit Profile </a-menu-item>
<a-menu-item key="profile"> View Profile </a-menu-item>
<a-menu-item key="stories"> Stories </a-menu-item>
<a-menu-item key="drafts"> Drafts </a-menu-item>
<a-menu-item key="reviews"> Manage Reviews </a-menu-item>
<a-menu-item key="messages"> Private Messages </a-menu-item>
<a-menu-item key="account"> Account</a-menu-item>
<a-menu-item key="edit-profile"> Edit Profile</a-menu-item>
<a-menu-item key="profile"> View Profile</a-menu-item>
<a-menu-item key="stories"> Stories</a-menu-item>
<a-menu-item key="drafts"> Drafts</a-menu-item>
<a-menu-item key="reviews"> Manage Reviews</a-menu-item>
<a-menu-item key="messages"> Private Messages</a-menu-item>
</a-sub-menu>
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin </a-menu-item>
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin</a-menu-item>
<a-menu-item key="logout" v-if="!!data?.user"> Logout</a-menu-item>
</a-menu>
<div v-if="data?.user">
<nuxt-link to="/new-story">
<a-button type="primary" tooltip="Post a New Story">
<a-button class="new-btn" type="primary" tooltip="Post a New Story" @click="() => navigateTo(`/new-story`)" v-if="data?.user">
<!-- <template #icon>
</template> -->
<icon istyle="regular" name="file-plus" />
<span style="margin-left: 0.5em"> Post a New Story </span>
</a-button>
</nuxt-link>
</div>
<div class="acbut" v-if="!data?.user">
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login </a-button>
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register </a-button>
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login</a-button>
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register</a-button>
</div>
</div>
</template>
@ -101,4 +98,11 @@
flex-grow: 1.2;
justify-content: stretch;
}
@media (min-width: 768px) {
.new-btn {
align-self: self-end;
margin-top: 1em;
}
}
</style>

View File

@ -4,7 +4,6 @@
import icon from "~/components/icon.vue";
const story = inject<SingleChapterResult | null>("story");
console.log("storyyy--info", story);
const dark = inject<Ref<boolean>>("dark");
</script>
<template>

1
index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="typings/node-ext.d.ts">

View File

@ -14,7 +14,7 @@ export const storyMiddleware = defineNuxtRouteMiddleware(async (to, from) => {
console.log("to n from", to, from, data);
const { data: story, error } = await useApiFetch<SingleChapterResult>(to.path);
if (error.value) {
if (error.value.message.toLocaleLowerCase() == "unauthenticated") {
if (error.value.message.toLocaleLowerCase() == "unauthenticated" || error.value.statusCode == "403") {
return navigateTo("/auth/login");
}
return showError(error.value);

View File

@ -1,9 +1,10 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
import { IUser } from "@models/user";
import { render } from "vue";
import { rc } from "./lib/server/constants";
import { defineNuxtConfig } from "nuxt/config";
import { fileURLToPath } from "url";
import { StorageMounts } from "nitropack";
import { uri } from "./lib/dbconfig";
const cac = {
isr: true,
@ -141,7 +142,11 @@ export default defineNuxtConfig({
runtimeConfig: rc,
typescript: {
tsConfig: {
exclude: ["./.nuxt/types/auth.d.ts"],
exclude: ["./.nuxt/types/auth.d.ts", "../**/node_modules/next/**/*.d.ts"],
compilerOptions: {
typeRoots: ["typings/**/*.d.ts"],
types: ["typings/node-ext.d.ts"],
},
},
},
imports: {

View File

@ -1,6 +1,7 @@
<script lang="ts" setup>
import { reactive } from "vue";
import { notification } from "ant-design-vue";
interface FormState {
username: string;
password: string;
@ -15,23 +16,26 @@
useHead({
title: "Log In",
});
const loadRef = ref<boolean>(false);
const formState = reactive<FormState>({
username: "",
password: "",
});
const darkRef = inject<Ref<boolean>>("dark");
const darkRef = inject<Ref<boolean>>("dark")!;
const onFinish = async (values: any) => {
const { signIn } = useAuth();
try {
loadRef.value = true;
await signIn(values, { redirect: true, callbackUrl: "/" });
const { data } = useAuth();
darkRef.value = data.value.user.profile.nightMode;
if (darkRef.value) document.body.dataset.theme = "dark";
darkRef!.value = data.value?.user?.profile.nightMode || false;
if (darkRef!.value) document.body.dataset.theme = "dark";
await navigateTo("/");
} catch (e: any) {
if (e.data) {
loadRef.value = false;
notification["error"]({
message: h("div", { innerHTML: e.data.message }),
});
@ -50,7 +54,7 @@
<a-form-item>
<a-row :justify="'center'" :align="'middle'">
<a-col>
<a-button data-testid="login.submit" type="primary" html-type="submit">Log in</a-button>
<a-button :loading="loadRef" data-testid="login.submit" type="primary" html-type="submit">Log in</a-button>
</a-col>
</a-row>
</a-form-item>

View File

@ -16,7 +16,6 @@
const rtr = useRoute();
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);
let dark = inject<boolean>("dark");
const and = computed(() => {

View File

@ -1,5 +1,5 @@
export default eventHandler((event) => {
let ahead = (getHeaders(event).authorization || "")?.replace("Bearer ", "");
let ahead = (getHeaders(event).authorization || getHeaders(event).Authorization || "")?.replace("Bearer ", "");
if (event.context.currentUser && ahead) {
return {
token: ahead,

View File

@ -1,26 +1,26 @@
import jwt from "jsonwebtoken";
import { log } from "@server/logger";
import { messages } from "@server/constants";
import { User } from "@models/user";
import { AccessToken } from "@models/oauth";
import { IJwt } from "@server/types/authstuff";
import { IUser, User } from "@models/user";
export default defineEventHandler(async (event) => {
let ahead = (getHeaders(event).authorization || "")?.replace("Bearer ", "");
let ahead = (getHeaders(event).authorization || getHeaders(event).Authorization || getCookie(event, "rockfic_cookie"))?.replace("Bearer ", "");
if (ahead) {
let toktok: jwt.JwtPayload;
let toktok: any;
let user: IUser | null = null;
try {
toktok = jwt.verify(ahead, useRuntimeConfig().jwt) as IJwt;
let user = await User.findById(toktok.id as number).exec();
if (user && toktok) event.context.currentUser = user;
} catch (e) {
const t = await AccessToken.findOne({ token: ahead });
if (!t)
throw createError({
statusCode: 401,
message: messages[401],
});
let user = await User.findById(t.userID);
toktok = jwt.verify(ahead, useRuntimeConfig().jwt);
console.log(toktok);
if (toktok?.sub) {
user = await User.findById(toktok.sub as number)
.select("-password -auth -ipLog")
.exec();
} else if (toktok.id) {
user = await User.findById(toktok.id as number)
.select("-password -auth -ipLog")
.exec();
}
} catch (E) {
console.error(E);
} finally {
if (user) event.context.currentUser = user;
}
}

View File

@ -8,6 +8,22 @@
"noImplicitThis": false,
"verbatimModuleSyntax": false,
"forceConsistentCasingInFileNames": false,
"allowSyntheticDefaultImports": true
"allowSyntheticDefaultImports": true,
"types": [
"./typings/fuck-you.ts",
"./typings/*.d.ts"
],
"typeRoots": [
"typings/**/*.d.ts",
"typings"
]
},
"exclude": [
"**/node_modules/next/**/*.d.ts"
],
"include": [
"typings/**/*.d.ts",
"typings/node-ext.d.ts",
"nuxt.config.ts"
]
}

7
typings/node-ext.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: ("development" | "staging" | "production" | "test") & (string & {});
}
}
}