From 8c56a9421e89b686fbdfaa2d38e371d8310841e3 Mon Sep 17 00:00:00 2001 From: Andreas Schaafsma Date: Sat, 25 May 2024 01:17:39 +0200 Subject: [PATCH] changes --- .vscode/launch.json | 28 ++++++ .vscode/settings.json | 4 + next.config.js | 2 +- src/api/error.ts | 16 ---- src/app/admin/layout.tsx | 24 +++++ src/app/admin/page.tsx | 6 +- src/app/api/auth/route.ts | 4 +- src/app/api/auth/validate/route.ts | 4 +- src/app/api/post/[...slug]/route.ts | 2 +- src/app/api/post/route.ts | 67 +++++--------- src/app/api/user/route.ts | 4 +- src/app/lib/actions.ts | 90 ++++++++++--------- src/components/client/admin/adminPanel.tsx | 62 +++++++++++-- src/components/client/admin/entryEditor.tsx | 9 ++ .../client/admin/views/ClientPostView.tsx | 16 ++++ src/components/client/admin/views/sidebar.css | 90 +++++++++++++++++++ src/components/client/admin/views/sidebar.tsx | 30 +++++++ src/components/server/ServerComponentTest.tsx | 0 src/components/server/admin/authHandler.tsx | 4 +- src/components/server/admin/authWrapper.tsx | 47 ++++++++++ .../server/admin/views/PostView.tsx | 21 +++++ src/model/Project.ts | 48 ++++------ src/model/Tag.ts | 6 +- src/model/User.ts | 5 +- src/providers/providers.tsx | 13 ++- src/util/api/error.ts | 28 ++++++ src/{ => util}/api/user.ts | 2 +- 27 files changed, 467 insertions(+), 165 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json delete mode 100644 src/api/error.ts create mode 100644 src/app/admin/layout.tsx create mode 100644 src/components/client/admin/entryEditor.tsx create mode 100644 src/components/client/admin/views/ClientPostView.tsx create mode 100644 src/components/client/admin/views/sidebar.css create mode 100644 src/components/client/admin/views/sidebar.tsx create mode 100644 src/components/server/ServerComponentTest.tsx create mode 100644 src/components/server/admin/authWrapper.tsx create mode 100644 src/components/server/admin/views/PostView.tsx create mode 100644 src/util/api/error.ts rename src/{ => util}/api/user.ts (95%) diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e330519 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev" + }, + { + "name": "Next.js: debug client-side", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "serverReadyAction": { + "pattern": "- Local:.+(https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] + } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..45c0476 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "window.title": "${dirty}${activeEditorMedium}${separator}${rootName}${separator}${profileName}${separator}${appName}", + "workbench.editor.labelFormat": "medium", +} \ No newline at end of file diff --git a/next.config.js b/next.config.js index 1a601fd..36983eb 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { experimental: { - serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core'], + serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core', '@/app/lib/api/error'], serverActions: { }, diff --git a/src/api/error.ts b/src/api/error.ts deleted file mode 100644 index ba86f72..0000000 --- a/src/api/error.ts +++ /dev/null @@ -1,16 +0,0 @@ - - -type APIErrorMessage = { - status: number, - responseText: string -} -class APIError extends Error{ - declare info:APIErrorMessage - constructor(message:APIErrorMessage) { - super(JSON.stringify(message)) - this.info = message; - this.name = "AuthError"; // (different names for different built-in error classes) - } -} - -export { APIError } \ No newline at end of file diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 0000000..5b4d00b --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,24 @@ +import '../globals.css' +import { Inter } from 'next/font/google' +import StyledJsxRegistry from '../registry'; +import Providers from '@/providers/providers'; +import 'bootstrap/dist/css/bootstrap.css'; + +const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] }) + +export const metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index ff1034b..c125274 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -5,9 +5,9 @@ import { cookies } from "next/headers"; export default async function Page(){ return ( -
-

this is only shown on the admin panel

-
{JSON.stringify(cookies().getAll())}
+
+ + {/*
{JSON.stringify(cookies().getAll())}
*/}
); } \ No newline at end of file diff --git a/src/app/api/auth/route.ts b/src/app/api/auth/route.ts index af4664d..d5829a9 100644 --- a/src/app/api/auth/route.ts +++ b/src/app/api/auth/route.ts @@ -2,8 +2,8 @@ import { cookies } from "next/headers"; -import { APIError} from "@/api/error" -import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user" +import { APIError} from "@/util/api/error" +import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user" import { Auth, User } from "@/model/Models"; diff --git a/src/app/api/auth/validate/route.ts b/src/app/api/auth/validate/route.ts index d4a58c9..3e84ac6 100644 --- a/src/app/api/auth/validate/route.ts +++ b/src/app/api/auth/validate/route.ts @@ -7,8 +7,8 @@ import { cookies } from "next/headers"; import { Auth } from "@/model/Models"; -import { APIError} from "@/api/error" -import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user" +import { APIError} from "@/util/api/error" +import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user" async function tryAuth(request:Request){ diff --git a/src/app/api/post/[...slug]/route.ts b/src/app/api/post/[...slug]/route.ts index 4720622..d493f59 100644 --- a/src/app/api/post/[...slug]/route.ts +++ b/src/app/api/post/[...slug]/route.ts @@ -1,6 +1,6 @@ 'use server' -import { APIError } from "@/api/error"; +import { APIError } from "@/app/lib/api/error"; import { Post } from "@/model/Models"; diff --git a/src/app/api/post/route.ts b/src/app/api/post/route.ts index adccadc..fcee590 100644 --- a/src/app/api/post/route.ts +++ b/src/app/api/post/route.ts @@ -1,6 +1,6 @@ 'use server' -import { APIError } from "@/api/error"; +import { APIError, attemptAPIAction } from "@/util/api/error"; import { Auth, Post, PostTag, Tag, User } from "@/model/Models"; import { cookies } from "next/headers"; @@ -25,55 +25,45 @@ async function tryCreatePost(request: Request) { const authObject = JSON.parse(cookieJSON); // Fetch User Auth from the database - const auth:any = await Auth.findOne({ include: 'user', where: { token: authObject.token } }); + const auth = await Auth.findOne({ + include: [ + { + model: User.withScope(['withPerms']), + attributes: { + exclude: ['username', 'password', 'updatedAt', 'createdAt'] + } + } + ], + where: { token: authObject.token } + }); // Sanity check the auth and associated user - if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" }); - const user = auth.user; + if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" }); // Handle incomplete data or other problems - if (!requestBody) throw new APIError({ status: 500, responseText: "Empty request body" }); - if (!requestBody.title) throw new APIError({ status: 500, responseText: "Missing post title" }); - if (!requestBody.content) throw new APIError({ status: 500, responseText: "Missing post content" }); - if (!user.id) throw new APIError({ status: 500, responseText: "Missing user id" }); - if (!user.perms || !user.perms.isAdmin) throw new APIError({ status: 401, responseText: "Unauthorized" }); + if (!requestBody) throw new APIError({ status: 500, responseText: "Empty request body" }); + if (!requestBody.title) throw new APIError({ status: 500, responseText: "Missing post title" }); + if (!requestBody.content) throw new APIError({ status: 500, responseText: "Missing post content" }); + if (!auth.user.id) throw new APIError({ status: 500, responseText: "Missing user id" }); + if (!auth.user.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` }); // Create a new Post in the database const post = await Post.create( { content: requestBody.content, - user_id: user.id, + user_id: auth.user.id, title: requestBody.title, },{ include: { association: Post.associations.user } }).then(post=>post.reload()) - // // Find the post (Unneeded but nice to check) - // const foundPost = await Post.findOne({ - // include: "user", - // where: { - // id: post.id - // } - // }) + // Return the response return new Response(JSON.stringify(post), { status: 200 }); } -export async function POST(request: Request) { - try { - return await tryCreatePost(request); - } - catch (e) { - if (e instanceof APIError) { - return new Response(e.info.responseText, { status: e.info.status }); - } - else { - throw e; - } - } -} async function tryFetchPosts(request: Request) { @@ -94,18 +84,9 @@ async function tryFetchPosts(request: Request) { } - export async function GET(request: Request) { - try { - return await tryFetchPosts(request); - } - catch (e) { - if (e instanceof APIError) { - return new Response(e.info.responseText, { status: e.info.status }); - } - else { - throw e; - } - } + return await attemptAPIAction(tryFetchPosts,request); +} +export async function POST(request: Request) { + return await attemptAPIAction(tryCreatePost,request); } - diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index 2e81565..a04fb3a 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -8,8 +8,8 @@ * * ================================================================================================= */ -import { APIError } from "@/api/error"; -import { UserAuth } from "@/api/user"; +import { APIError } from "@/util/api/error"; +import { UserAuth } from "@/util/api/user"; import { UserPerms, User, Auth } from "@/model/Models"; // Do not alter "unused" imports, they are required to perform the nescessairy includes on the User model import { hashPassword } from "@/util/Auth"; diff --git a/src/app/lib/actions.ts b/src/app/lib/actions.ts index 990054d..6045d87 100644 --- a/src/app/lib/actions.ts +++ b/src/app/lib/actions.ts @@ -1,68 +1,66 @@ 'use server' import { constructAPIUrl } from "@/util/Utils" -// import { signIn } from '@/auth' -import { cookies } from "next/headers" +import { cookies } from "next/headers" import { parseSetCookie } from "@/util/parseSetCookie"; -import makeFetchCookie from 'fetch-cookie'; -import fetchCookie from "fetch-cookie"; +import makeFetchCookie from 'fetch-cookie'; +import fetchCookie from "fetch-cookie"; +import { Attributes } from "@sequelize/core"; +import { User } from "@/model/User"; type LoginReturn = { cookie?:unknown, errorMessage?:string; } -async function attemptAPILogin(method:string,b:FormData):Promise +async function attemptAPILogin(method:string,formData:FormData):Promise { - console.log("form data:"); - console.log(b); - if(!b || !b.get('input_username') || !b.get('input_password')) return null; - let headers:Headers = new Headers(); - const { CookieJar, Cookie } = fetchCookie.toughCookie; - const cj = new CookieJar() - const fetchKoek = makeFetchCookie(fetch, cj); - // Set Basic Auth - headers.set('Authorization', `Basic ${Buffer.from(`${b.get('input_username')}:${b.get('input_password')}`).toString('base64')}`); - let res = await fetchKoek(constructAPIUrl("auth"), { - method:'POST', - credentials: 'include', - headers:headers, - }); - console.log(cj.store.idx['localhost']['/']); - // if(res.headers.get('set-coo')) console.log(res.headers['set-cookie']) - let koek = res.headers.getSetCookie(); - // console.log("api koek:" + koek.map((k)=>decodeURIComponent(k))); - let cookieDict = parseSetCookie(koek); - console.log("testing cookiedict") - console.log(cookieDict); - await cookies().set('auth', cookieDict.auth); - return { - cookie:cookieDict.auth, - errorMessage:"" - }; - // console.log(koek); + // Check if form data is present with required fields, return null if not + if(!formData || !formData.get('input_username') || !formData.get('input_password')) return null; + + // Instantiate header object + let headers:Headers = new Headers(); + + // Prepare fetchCookie + const { CookieJar, Cookie } = fetchCookie.toughCookie; + const jar = new CookieJar() + const fetchWithCookie = makeFetchCookie(fetch, jar); + + // Set Basic Auth + headers.set('Authorization', `Basic ${Buffer.from(`${formData.get('input_username')}:${formData.get('input_password')}`).toString('base64')}`); + let res = await fetchWithCookie(constructAPIUrl("auth"), { + method:'POST', + credentials: 'include', + headers:headers, + }); + + console.log(jar.store.idx['localhost']['/']); + + let koek = res.headers.getSetCookie(); + + let cookieDict = parseSetCookie(koek); + + await cookies().set('auth', cookieDict.auth); + return { + cookie:cookieDict.auth, + errorMessage:"" + }; + // console.log(koek); } export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise { - console.log("action triggered") - try { + try { const signInStatus = await attemptAPILogin('credentials', formData) return signInStatus; } catch (error:any) { if (error) { switch (error.type) { - case 'CredentialsSignin': - return { - errorMessage: 'invalidCredentials' - } - default: - return { - errorMessage: 'Something went wrong.' - } + case 'CredentialsSignin': return { errorMessage: 'invalidCredentials' }; + default: return { errorMessage: 'Something went wrong.' }; } } - throw error + throw Error } } @@ -78,4 +76,10 @@ export async function serverValidateSessionCookie(koek:string):Promise return true else return false +} + +export async function userIsAdmin(user:Partial>):Promise +{ + + return false; } \ No newline at end of file diff --git a/src/components/client/admin/adminPanel.tsx b/src/components/client/admin/adminPanel.tsx index 610021f..d10afb7 100644 --- a/src/components/client/admin/adminPanel.tsx +++ b/src/components/client/admin/adminPanel.tsx @@ -1,23 +1,67 @@ 'use client' -import { ReactNode, useContext } from "react"; +import { ReactNode, useContext, useState } from "react"; import { AuthContext, AuthProps } from "@/providers/providers"; import SomeServerSubComponent from "@/components/server/admin/serverContextUserTest"; +import PostView from "@/components/server/admin/views/PostView"; +import Sidebar from "./views/sidebar"; +import ClientPostView from "./views/ClientPostView"; +import ServerComponentTest from "@/components/server/ServerComponentTest"; + interface Props { children?: ReactNode; auth?: AuthProps; } + +export type SidebarEntry = { + label:string; + view:string; +} + + +export const sidebarEntries:Array = [ + { label: 'Home', view: 'home'}, + { label: 'Post Management', view: 'man-post'}, + { label: 'Project Management', view: 'man-proj'}, + { label: 'Tag Management', view: 'man-tags'}, + { label: 'User Management', view: 'man-user'}, +] + +function Home(){ + return
home
+} +function PostManager(){ + return
posts
+} + + +const viewMap = new Map([ + ['home', ], + ['man-post', ] +]) + + + export default function AdminPanel(props:Props){ - + const [view, setView] = useState(() as ReactNode); + + const switchView = function (value:string){ + setView(viewMap.get(value)); + } + return ( -
-

Super Secret Admin Panel!

-

this is where we use the context test:

- -
- {props.children} -
+
+ {/*

Super Secret Admin Panel!

*/} + {/*

this is where we use the context test:

*/} + {/* */} + {/*
*/} + {/* {props.children} */} + {/* */} + {/*
*/} + +
{view}
+
) } diff --git a/src/components/client/admin/entryEditor.tsx b/src/components/client/admin/entryEditor.tsx new file mode 100644 index 0000000..6a9e9b6 --- /dev/null +++ b/src/components/client/admin/entryEditor.tsx @@ -0,0 +1,9 @@ + + +export default function EntryEditor(){ + return ( +
+ Entries +
+ ); +} \ No newline at end of file diff --git a/src/components/client/admin/views/ClientPostView.tsx b/src/components/client/admin/views/ClientPostView.tsx new file mode 100644 index 0000000..4e82fd5 --- /dev/null +++ b/src/components/client/admin/views/ClientPostView.tsx @@ -0,0 +1,16 @@ +'use client' + +import ServerComponentTest from "@/components/server/ServerComponentTest"; +import { ReactNode } from "react"; + +type Props = { + children?:ReactNode +} + +export default function ClientPostView(props:Props){ + return ( +
+ test +
+ ); +} \ No newline at end of file diff --git a/src/components/client/admin/views/sidebar.css b/src/components/client/admin/views/sidebar.css new file mode 100644 index 0000000..8c630a5 --- /dev/null +++ b/src/components/client/admin/views/sidebar.css @@ -0,0 +1,90 @@ +body { + min-height: 100vh; + min-height: -webkit-fill-available; + } + + html { + height: -webkit-fill-available; + } + + main { + display: flex; + flex-wrap: nowrap; + height: 100vh; + height: -webkit-fill-available; + max-height: 100vh; + overflow-x: auto; + overflow-y: hidden; + } + + .b-example-divider { + flex-shrink: 0; + width: 1.5rem; + height: 100vh; + background-color: rgba(0, 0, 0, .1); + border: solid rgba(0, 0, 0, .15); + border-width: 1px 0; + box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); + } + + .bi { + vertical-align: -.125em; + pointer-events: none; + fill: currentColor; + } + + .dropdown-toggle { outline: 0; } + + .nav-flush .nav-link { + border-radius: 0; + } + + .btn-toggle { + display: inline-flex; + align-items: center; + padding: .25rem .5rem; + font-weight: 600; + color: rgba(0, 0, 0, .65); + background-color: transparent; + border: 0; + } + .btn-toggle:hover, + .btn-toggle:focus { + color: rgba(0, 0, 0, .85); + background-color: #d2f4ea; + } + + .btn-toggle::before { + width: 1.25em; + line-height: 0; + content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); + transition: transform .35s ease; + transform-origin: .5em 50%; + } + + .btn-toggle[aria-expanded="true"] { + color: rgba(0, 0, 0, .85); + } + .btn-toggle[aria-expanded="true"]::before { + transform: rotate(90deg); + } + + .btn-toggle-nav a { + display: inline-flex; + padding: .1875rem .5rem; + margin-top: .125rem; + margin-left: 1.25rem; + text-decoration: none; + } + .btn-toggle-nav a:hover, + .btn-toggle-nav a:focus { + background-color: #d2f4ea; + } + + .scrollarea { + overflow-y: auto; + } + + .fw-semibold { font-weight: 600; } + .lh-tight { line-height: 1.25; } + \ No newline at end of file diff --git a/src/components/client/admin/views/sidebar.tsx b/src/components/client/admin/views/sidebar.tsx new file mode 100644 index 0000000..03d86d3 --- /dev/null +++ b/src/components/client/admin/views/sidebar.tsx @@ -0,0 +1,30 @@ +import './sidebar.css' +import { Button, NavLink } from 'react-bootstrap'; +import { SidebarEntry } from '../adminPanel'; +import React, { ReactNode, useState } from 'react'; + + + + +type Props = { + children?:ReactNode; + sidebarEntries:Array; + switchView:any; +} + + + +export default function Sidebar(props:Props){ + + return ( +
+
    + {props.sidebarEntries.map((sidebarEntry)=>{ + return
  • {props.switchView(sidebarEntry.view)}}> + {sidebarEntry.label} +
  • + })} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/server/ServerComponentTest.tsx b/src/components/server/ServerComponentTest.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/server/admin/authHandler.tsx b/src/components/server/admin/authHandler.tsx index 87c1027..940ab83 100644 --- a/src/components/server/admin/authHandler.tsx +++ b/src/components/server/admin/authHandler.tsx @@ -40,8 +40,8 @@ export default async function AuthHandler(props: Props) { } return ( -
- {!(koek && await serverValidateSessionCookie(koek)) ? { } :
{props.children}
signed in! :D
} +
+ {!(koek && await serverValidateSessionCookie(koek)) ? { } : {props.children}}
); } diff --git a/src/components/server/admin/authWrapper.tsx b/src/components/server/admin/authWrapper.tsx new file mode 100644 index 0000000..f8ed201 --- /dev/null +++ b/src/components/server/admin/authWrapper.tsx @@ -0,0 +1,47 @@ +'use server' +import { cookies } from "next/headers"; + +import LoginForm from "@/components/client/admin/loginForm"; +import AdminPanel from "@/components/client/admin/adminPanel"; +import ClientAuthHandler from "@/components/client/admin/clientAuthHandler"; + +import { serverValidateSessionCookie } from "@/app/lib/actions"; +import { constructAPIUrl } from "@/util/Utils"; +import { ReactNode } from "react"; +import { AuthContext, AuthProps } from "@/providers/providers"; + + +interface Props { + children?: ReactNode; + params?: any; + requiredRole?: number; +} // We interfacing lads + + +export default async function AuthHandler(props: Props) { + const protoKoek = await cookies().get('auth')?.value; + const koek = decodeURIComponent(protoKoek ? protoKoek: ""); + console.log("koekje:" + koek) + let p:AuthProps = { + test:"not pog" + }; + if(koek){ + const kd = JSON.parse(koek); + if(kd.id && kd.token && kd.user_id){ + p = { + test:"success!", + auth: { + id:kd.id, + token:kd.token, + user_id:kd.user_id + } + } + } + } + + return ( +
+ {!(koek && await serverValidateSessionCookie(koek)) ? { } :
{props.children}
} +
+ ); +} diff --git a/src/components/server/admin/views/PostView.tsx b/src/components/server/admin/views/PostView.tsx new file mode 100644 index 0000000..4d3a246 --- /dev/null +++ b/src/components/server/admin/views/PostView.tsx @@ -0,0 +1,21 @@ +'use server' + +import { ReactNode } from "react" +import AuthHandler from "../authHandler" +import { Post } from "@/model/Post" +import { constructAPIUrl } from "@/util/Utils" + +type Props = { + children?:ReactNode +} + +export default async function PostView(props:Props){ + // const posts = await Post.findAll(); + + + return ( +
+ test +
+ ); +} \ No newline at end of file diff --git a/src/model/Project.ts b/src/model/Project.ts index 178e384..cfe60b2 100644 --- a/src/model/Project.ts +++ b/src/model/Project.ts @@ -1,11 +1,9 @@ import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User"; import { SqliteDialect } from '@sequelize/sqlite3'; +import { Attribute, AutoIncrement, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy"; +import { Post } from "./Post"; -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite' -}); export type ProjectAttributes = { id: number; title: string; @@ -20,37 +18,21 @@ export type ProjectCreationAttributes = { user_id:number; } export class Project extends Model { + @Attribute(DataTypes.INTEGER) @PrimaryKey @Unique @AutoIncrement declare id: number; - declare user:NonAttribute; - declare user_id:ForeignKey; - declare getUser: BelongsToGetAssociationMixin; + @Attribute(DataTypes.STRING) @Unique + declare readableIdentifier: string; + @Attribute(DataTypes.STRING) + declare name: string; + declare static associations: { - user: Association; + posts: Association; + }; + } -Project.init( - { - id: { - allowNull: false, - autoIncrement: true, - type: DataTypes.INTEGER, - primaryKey: true, - unique: true, - }, - content: { - type: DataTypes.STRING - }, - title: { - type: DataTypes.STRING - }, - date: { - type: DataTypes.DATE - }, - - }, - { - tableName: 'Projects', - sequelize // passing the `sequelize` instance is required - } -); \ No newline at end of file +const sequelize = new Sequelize({ + dialect: SqliteDialect, + models: [Project] +}) diff --git a/src/model/Tag.ts b/src/model/Tag.ts index ea99f8f..8ec5ab0 100644 --- a/src/model/Tag.ts +++ b/src/model/Tag.ts @@ -10,12 +10,10 @@ export class Tag extends Model, InferCreationAttributes; - - declare user_id:ForeignKey; - declare getPost: BelongsToManyGetAssociationsMixin; + declare getPosts: BelongsToManyGetAssociationsMixin; + declare static associations: { posts: Association; }; diff --git a/src/model/User.ts b/src/model/User.ts index 0b222ce..723dec5 100644 --- a/src/model/User.ts +++ b/src/model/User.ts @@ -55,9 +55,10 @@ export class User extends Model, InferCreationAttributes + declare static associations: { - perms: Association; - posts: Association + perms: Association; + posts: Association authtokens: Association; }; diff --git a/src/providers/providers.tsx b/src/providers/providers.tsx index ec63481..57dfb17 100644 --- a/src/providers/providers.tsx +++ b/src/providers/providers.tsx @@ -15,8 +15,19 @@ let p: AuthProps = { test: "lorem", } +export type AdminViewProps = { + view: string; +} + +let avp: AdminViewProps = { + view: "home", +} + +export const AdminViewContext = createContext(avp); export const AuthContext = createContext(p); + + interface Props { children?: ReactNode; params?: any; @@ -24,6 +35,6 @@ interface Props { export default function Providers(props:Props){ return ( - {props.children} + {props.children} ) } \ No newline at end of file diff --git a/src/util/api/error.ts b/src/util/api/error.ts new file mode 100644 index 0000000..716b6b2 --- /dev/null +++ b/src/util/api/error.ts @@ -0,0 +1,28 @@ +type APIErrorMessage = { + status: number, + responseText: string +} +class APIError extends Error{ + declare info:APIErrorMessage + constructor(message:APIErrorMessage) { + super(JSON.stringify(message)) + this.info = message; + this.name = "AuthError"; // (different names for different built-in error classes) + } +} + +const attemptAPIAction = async (action:Function,request:Request) => { + try { + return await action(request); + } + catch (e) { + if (e instanceof APIError) { + return new Response(e.info.responseText, { status: e.info.status }); + } + else { + throw e; + } + } +} + +export { APIError, attemptAPIAction } \ No newline at end of file diff --git a/src/api/user.ts b/src/util/api/user.ts similarity index 95% rename from src/api/user.ts rename to src/util/api/user.ts index f9a0c37..a56f4de 100644 --- a/src/api/user.ts +++ b/src/util/api/user.ts @@ -2,7 +2,7 @@ // import { MAuth } from "@/model/sequelize/Auth"; import { validatePassword } from "@/util/Auth"; -import { APIError } from "@/api/error"; +import { APIError } from "@/util/api/error"; import { User } from "@/model/User"; export function parseBasicAuth(authb64:string):UserAuth