Merge branch 'master' of https://git.subsonics.nl/andreas/portfolio2023
This commit is contained in:
commit
33aef5e2f7
File diff suppressed because one or more lines are too long
@ -1,25 +1,30 @@
|
||||
// import '../globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
import StyledJsxRegistry from '../registry';
|
||||
import Providers from '@/providers/providers';
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { Inter } from "next/font/google";
|
||||
import StyledJsxRegistry from "../registry";
|
||||
import ContextProviders from "@/providers/contextproviders";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] })
|
||||
const inter = Inter({ subsets: ["latin"], fallback: ["system-ui", "arial"] });
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}><Toaster position="bottom-center" /><StyledJsxRegistry><Providers>{children}</Providers></StyledJsxRegistry></body>
|
||||
</html>
|
||||
)
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<Toaster position="bottom-center" />
|
||||
<StyledJsxRegistry>
|
||||
<ContextProviders>{children}</ContextProviders>
|
||||
</StyledJsxRegistry>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
11
src/app/api-doc/page.tsx
Normal file
11
src/app/api-doc/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { getApiDocs } from "@/lib/swagger";
|
||||
import ReactSwagger from "@/components/client/SwaggerComponent";
|
||||
|
||||
export default async function IndexPage() {
|
||||
const spec = await getApiDocs();
|
||||
return (
|
||||
<section className="container">
|
||||
<ReactSwagger spec={spec} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
"use server";
|
||||
|
||||
import { APIError, attemptAPIAction } from "@/util";
|
||||
import {
|
||||
sequelize,
|
||||
Bucket,
|
||||
@ -20,6 +19,7 @@ import {
|
||||
} from "@/app/lib/actions/entitymanagement/attachment/attachmentActions";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
import { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies";
|
||||
import { APIError, attemptAPIAction } from "@/util/api";
|
||||
|
||||
export async function tryCreateAttachment(request: Request) {
|
||||
// Make sure the DB is ready
|
||||
@ -127,6 +127,17 @@ export async function tryCreateAttachment(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/attachment:
|
||||
* get:
|
||||
* description: Returns all attachments
|
||||
* responses:
|
||||
* 200:
|
||||
* description: idk
|
||||
*/
|
||||
export async function tryFetchAttachments(request: Request) {
|
||||
await Post.sync();
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
"use server";
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
import { APIError, UserAuth, parseBasicAuth, getAssociatedUser } from "@/util";
|
||||
import { Auth, User } from "@/models";
|
||||
import { APIError, getAssociatedUser, parseBasicAuth } from "@/util/api";
|
||||
|
||||
async function tryAuth(request: Request) {
|
||||
// await User.sync();
|
||||
@ -63,11 +63,12 @@ async function tryAuth(request: Request) {
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
return await tryAuth(request);
|
||||
} catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
return new Response(e.info.responseText, { status: e.info.status });
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch(e){
|
||||
if (e instanceof APIError){
|
||||
return new Response(e.info.responseText,{status:e.info.status});
|
||||
}
|
||||
else{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,159 +1,189 @@
|
||||
'use server'
|
||||
"use server";
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
import { APIError} from "@/util/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
||||
import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models";
|
||||
import { APIError } from "@/util/api/error";
|
||||
import {
|
||||
Attachment,
|
||||
Auth,
|
||||
Bucket,
|
||||
DBState,
|
||||
Post,
|
||||
PostTag,
|
||||
Project,
|
||||
Tag,
|
||||
User,
|
||||
UserPerms,
|
||||
dbSync,
|
||||
sequelize,
|
||||
} from "@/models";
|
||||
import Sequelize, { CreationAttributes, DataTypes } from "@sequelize/core";
|
||||
import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3";
|
||||
import {
|
||||
SqliteColumnsDescription,
|
||||
SqliteDialect,
|
||||
SqliteQueryInterface,
|
||||
} from "@sequelize/sqlite3";
|
||||
import { hashpassword } from "@/util/auth";
|
||||
import { copyFile, readFileSync } from "fs";
|
||||
import path from "path";
|
||||
import { Attributes } from '@sequelize/core';
|
||||
import { DeepPartial } from "@/util/DeepPartial";
|
||||
import { UUID } from "crypto";
|
||||
import { runApiAction } from "@/util/api";
|
||||
|
||||
async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>){
|
||||
const fp = path.resolve('./db/seed/users.json');
|
||||
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString());
|
||||
async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>) {
|
||||
const fp = path.resolve("./db/seed/users.json");
|
||||
const json: { users: CreationAttributes<User>[] } = JSON.parse(
|
||||
Buffer.from(readFileSync(fp).valueOf()).toString()
|
||||
);
|
||||
|
||||
const users = json.users.map(async user=>{
|
||||
user.password = await hashpassword(user.password);
|
||||
return user;
|
||||
})
|
||||
const users = json.users.map(async (user) => {
|
||||
user.password = await hashpassword(user.password);
|
||||
return user;
|
||||
});
|
||||
|
||||
const dbUsers = await User.bulkCreate(await Promise.all(users), {include: User.associations.perms})
|
||||
const dbUsers = await User.bulkCreate(await Promise.all(users), {
|
||||
include: User.associations.perms,
|
||||
});
|
||||
}
|
||||
|
||||
async function seedPosts(qif: SqliteQueryInterface<SqliteDialect>){
|
||||
const fp = path.resolve('./db/seed/posts.json');
|
||||
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString());
|
||||
const projects =[
|
||||
{
|
||||
"name": "Blog",
|
||||
"readableIdentifier": "blog",
|
||||
"posts": [
|
||||
{
|
||||
"title": "Test Post",
|
||||
"content": "# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n",
|
||||
"description": "A new post to test the blog system",
|
||||
"project_id": 1,
|
||||
"user_id": 1,
|
||||
"buckets": [
|
||||
{
|
||||
"id": "788dfc19-55ba-482c-8124-277702296dfb",
|
||||
"attachments": [
|
||||
{
|
||||
"filename": "FB_IMG_1716665756868.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
projects.map(project=>{
|
||||
Project.create({name: project.name, readableIdentifier: project.readableIdentifier});
|
||||
project.posts.map(async post=>{
|
||||
const pst = await Post.create({title:post.title, content:post.content, description:post.description, project_id: post.project_id, user_id: post.user_id});
|
||||
post.buckets.map(async bucket=>{
|
||||
pst.createBucket({id:bucket.id as UUID});
|
||||
bucket.attachments.map(attachment=>{
|
||||
Attachment.create({bucket_id:bucket.id as UUID, filename: attachment.filename}).then((a)=>{
|
||||
copyFile(
|
||||
path.resolve('.','db','seed','post',a.filename),
|
||||
path.resolve('.','bucket',bucket.id,a.filename),
|
||||
()=>{
|
||||
async function seedPosts(qif: SqliteQueryInterface<SqliteDialect>) {
|
||||
const fp = path.resolve("./db/seed/posts.json");
|
||||
const json: { users: CreationAttributes<User>[] } = JSON.parse(
|
||||
Buffer.from(readFileSync(fp).valueOf()).toString()
|
||||
);
|
||||
const projects = [
|
||||
{
|
||||
name: "Blog",
|
||||
readableIdentifier: "blog",
|
||||
posts: [
|
||||
{
|
||||
title: "Test Post",
|
||||
content:
|
||||
"# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n",
|
||||
description: "A new post to test the blog system",
|
||||
project_id: 1,
|
||||
user_id: 1,
|
||||
buckets: [
|
||||
{
|
||||
id: "788dfc19-55ba-482c-8124-277702296dfb",
|
||||
attachments: [
|
||||
{
|
||||
filename: "FB_IMG_1716665756868.jpg",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
projects.map((project) => {
|
||||
Project.create({
|
||||
name: project.name,
|
||||
readableIdentifier: project.readableIdentifier,
|
||||
});
|
||||
project.posts.map(async (post) => {
|
||||
const pst = await Post.create({
|
||||
title: post.title,
|
||||
content: post.content,
|
||||
description: post.description,
|
||||
project_id: post.project_id,
|
||||
user_id: post.user_id,
|
||||
});
|
||||
post.buckets.map(async (bucket) => {
|
||||
pst.createBucket({ id: bucket.id as UUID });
|
||||
bucket.attachments.map((attachment) => {
|
||||
Attachment.create({
|
||||
bucket_id: bucket.id as UUID,
|
||||
filename: attachment.filename,
|
||||
}).then((a) => {
|
||||
copyFile(
|
||||
path.resolve(".", "db", "seed", "post", a.filename),
|
||||
path.resolve(".", "bucket", bucket.id, a.filename),
|
||||
() => {}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const project = await Project.findOne({where: {
|
||||
readableIdentifier: 'blog'
|
||||
}}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'}));
|
||||
const project = await Project.findOne({
|
||||
where: {
|
||||
readableIdentifier: "blog",
|
||||
},
|
||||
}).then((e) =>
|
||||
e
|
||||
? e
|
||||
: Project.create({
|
||||
name: "General Blog",
|
||||
readableIdentifier: "blog",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function seedDatabase(qif: SqliteQueryInterface<SqliteDialect>) {
|
||||
await seedUsers(qif);
|
||||
await seedPosts(qif);
|
||||
|
||||
|
||||
|
||||
|
||||
async function seedDatabase(qif:SqliteQueryInterface<SqliteDialect>){
|
||||
await seedUsers(qif);
|
||||
await seedPosts(qif)
|
||||
|
||||
// await Post.create({
|
||||
// title: 'Test Post',
|
||||
// content: `
|
||||
// # Hello <ExampleComponent />
|
||||
// this is some **test** markdown
|
||||
// `,
|
||||
// project_id: project.id,
|
||||
// user_id: user.id
|
||||
// })
|
||||
// await Post.create({
|
||||
// title: 'Test Post 2',
|
||||
// content: `
|
||||
// # Hello <ExampleComponent />
|
||||
// this is amother post with some **test** markdown
|
||||
// `,
|
||||
// project_id: project.id,
|
||||
// user_id: user.id
|
||||
// })
|
||||
// await Post.create({
|
||||
// title: 'Test Post',
|
||||
// content: `
|
||||
// # Hello <ExampleComponent />
|
||||
// this is some **test** markdown
|
||||
// `,
|
||||
// project_id: project.id,
|
||||
// user_id: user.id
|
||||
// })
|
||||
// await Post.create({
|
||||
// title: 'Test Post 2',
|
||||
// content: `
|
||||
// # Hello <ExampleComponent />
|
||||
// this is amother post with some **test** markdown
|
||||
// `,
|
||||
// project_id: project.id,
|
||||
// user_id: user.id
|
||||
// })
|
||||
}
|
||||
|
||||
async function trySetup(request:Request){
|
||||
await dbSync;
|
||||
export async function GET(request: Request) {
|
||||
runApiAction(async (request) => {
|
||||
await dbSync;
|
||||
|
||||
const queryInterface = sequelize.queryInterface
|
||||
const queryInterface = sequelize.queryInterface;
|
||||
|
||||
const version = (await DBState.findAll())
|
||||
.sort((a, b) => (a.version > b.version ? 1 : -1))
|
||||
.map((a) => a.version)[0];
|
||||
|
||||
const version = (await DBState.findAll()).sort((a,b)=> ((a.version > b.version) ? 1 : -1)).map(a=>a.version)[0];
|
||||
switch (version) {
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
seedDatabase(queryInterface);
|
||||
const postsRows: SqliteColumnsDescription = await queryInterface
|
||||
.describeTable(Post.table.tableName)
|
||||
.then((t) => t);
|
||||
if (!postsRows["project_id"])
|
||||
queryInterface.addColumn("Posts", "project_id", {
|
||||
type: DataTypes.INTEGER,
|
||||
acceptsNull: () => false,
|
||||
defaultValue: 1,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch(version){
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
seedDatabase(queryInterface);
|
||||
const postsRows:SqliteColumnsDescription = await queryInterface.describeTable(Post.table.tableName).then(t=>t);
|
||||
if (!postsRows['project_id']) queryInterface.addColumn('Posts','project_id',{type: DataTypes.INTEGER, acceptsNull:()=>false,defaultValue:1})
|
||||
break;
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify(
|
||||
{
|
||||
test: await queryInterface.describeTable('Posts').then(t=>t)
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers:{
|
||||
"Content-Type": "text/JSON"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function GET(request:Request){
|
||||
try{
|
||||
return await trySetup(request);
|
||||
}
|
||||
catch(e){
|
||||
if (e instanceof APIError){
|
||||
return new Response(e.info.responseText,{status:e.info.status});
|
||||
}
|
||||
else{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
test: await queryInterface
|
||||
.describeTable("Posts")
|
||||
.then((t) => t),
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "text/JSON",
|
||||
},
|
||||
}
|
||||
);
|
||||
}, request);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ async function attemptGetUsers(request:Request){
|
||||
);
|
||||
}
|
||||
|
||||
export async function GET(request:Request){
|
||||
export async function GET(request:Request): Promise<Response>{
|
||||
try{
|
||||
return await attemptGetUsers(request);
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import "@/app/index.css"
|
||||
|
||||
import { Post } from "@/models";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
import { DeepPartial } from "@/util/DeepPartial";
|
||||
import { DeepPartial } from "@/util/deeppartial";
|
||||
|
||||
|
||||
async function getData(slug:string):Promise<Attributes<Post>> {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import './globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
import StyledJsxRegistry from './registry';
|
||||
import Providers from '@/providers/providers';
|
||||
import ContextProviders from '@/providers/contextproviders';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] })
|
||||
|
||||
@ -17,7 +17,7 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}><StyledJsxRegistry><Providers>{children}</Providers></StyledJsxRegistry></body>
|
||||
<body className={inter.className}><StyledJsxRegistry><ContextProviders>{children}</ContextProviders></StyledJsxRegistry></body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,129 +1,154 @@
|
||||
'use server'
|
||||
"use server";
|
||||
|
||||
import { constructAPIUrl } from "@/util/url";
|
||||
import { cookies } from "next/headers"
|
||||
import { parseSetCookie } from "@/util/parseSetCookie";
|
||||
import makeFetchCookie from 'fetch-cookie';
|
||||
import fetchCookie from "fetch-cookie";
|
||||
import { Attribute, Attributes } from "@sequelize/core";
|
||||
import { User, Auth } from "@/models";
|
||||
import { AuthProps } from "@/providers/providers";
|
||||
import { ActionResult } from "./ActionResult";
|
||||
import { cookies } from "next/headers";
|
||||
import { parseSetCookie } from "@/util/cookies";
|
||||
import makeFetchCookie from "fetch-cookie";
|
||||
import fetchCookie from "fetch-cookie";
|
||||
import { User, Auth } from "@/models";
|
||||
import { AuthContextProviderProps } from "@/providers";
|
||||
|
||||
type LoginReturn = {
|
||||
cookie?:unknown,
|
||||
errorMessage?:string;
|
||||
cookie?: unknown;
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
async function attemptAPILogin(
|
||||
method: string,
|
||||
formData: FormData
|
||||
): Promise<LoginReturn | null> {
|
||||
// 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(await 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);
|
||||
}
|
||||
|
||||
async function attemptAPILogin(method:string,formData:FormData):Promise<LoginReturn|null>
|
||||
{
|
||||
// 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<LoginReturn | null> {
|
||||
try {
|
||||
const signInStatus = await attemptAPILogin("credentials", formData);
|
||||
return signInStatus;
|
||||
} catch (error: any) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
switch (error.type) {
|
||||
case "CredentialsSignin":
|
||||
return { errorMessage: "invalidCredentials" };
|
||||
default:
|
||||
return { errorMessage: "Something went wrong." };
|
||||
}
|
||||
}
|
||||
throw Error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise<LoginReturn|null>
|
||||
{
|
||||
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.' };
|
||||
}
|
||||
}
|
||||
throw Error
|
||||
}
|
||||
export async function serverValidateSessionCookie(
|
||||
koek: string
|
||||
): Promise<boolean> {
|
||||
const validateSession = await fetch(
|
||||
await constructAPIUrl("auth/validate"),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Cookie: `auth=${koek};`,
|
||||
},
|
||||
}
|
||||
);
|
||||
if (validateSession.status == 200) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
export async function serverValidateSessionCookie(koek:string):Promise<boolean>
|
||||
{
|
||||
const validateSession = await fetch(constructAPIUrl("auth/validate"),{
|
||||
method:"POST",
|
||||
headers:{
|
||||
Cookie: `auth=${koek};`
|
||||
}
|
||||
});
|
||||
if(validateSession.status == 200)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
export async function userIsAdmin(): Promise<boolean> {
|
||||
const cookieAuthValue = await cookies().get("auth")?.value;
|
||||
const cookieAuthSanitized = cookieAuthValue
|
||||
? JSON.parse(JSON.stringify(cookieAuthValue))
|
||||
: "";
|
||||
|
||||
if (!cookieAuthSanitized) return false;
|
||||
const parsedAuth = JSON.parse(cookieAuthSanitized);
|
||||
|
||||
if (!parsedAuth.id || !parsedAuth.token || !parsedAuth.user_id)
|
||||
return false;
|
||||
|
||||
const p: AuthContextProviderProps = {
|
||||
auth: {
|
||||
id: parsedAuth.id,
|
||||
token: parsedAuth.token,
|
||||
user_id: parsedAuth.user_id,
|
||||
},
|
||||
};
|
||||
|
||||
const foundAuth = await Auth.findOne({ where: { id: p.auth?.id } });
|
||||
if (!foundAuth || foundAuth.token != p.auth?.token) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function userIsAdmin():Promise<boolean>
|
||||
{
|
||||
const cookieAuthValue = await cookies().get('auth')?.value;
|
||||
const cookieAuthSanitized = cookieAuthValue? JSON.parse(JSON.stringify(cookieAuthValue)) : "";
|
||||
export async function getCookieAuth(): Promise<AuthContextProviderProps> {
|
||||
const cookieAuthValue = await cookies().get("auth")?.value;
|
||||
const cookieAuthSanitized = cookieAuthValue
|
||||
? JSON.parse(JSON.stringify(cookieAuthValue))
|
||||
: "";
|
||||
console.log("kanker koek");
|
||||
|
||||
if(!cookieAuthSanitized) return false;
|
||||
const parsedAuth = JSON.parse(cookieAuthSanitized);
|
||||
if (!cookieAuthSanitized) return {};
|
||||
|
||||
if(!parsedAuth.id || !parsedAuth.token || !parsedAuth.user_id) return false
|
||||
const kd = JSON.parse(cookieAuthSanitized);
|
||||
if (!kd.id || !kd.token || !kd.user_id) return {};
|
||||
|
||||
const p:AuthProps = {
|
||||
auth: {
|
||||
id:parsedAuth.id,
|
||||
token:parsedAuth.token,
|
||||
user_id:parsedAuth.user_id
|
||||
}
|
||||
};
|
||||
|
||||
const foundAuth = await Auth.findOne({where: { id: p.auth?.id}});
|
||||
if(!foundAuth || foundAuth.token != p.auth?.token ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getCookieAuth():Promise<AuthProps>
|
||||
{
|
||||
const cookieAuthValue = await cookies().get('auth')?.value;
|
||||
const cookieAuthSanitized = cookieAuthValue? JSON.parse(JSON.stringify(cookieAuthValue)) : "";
|
||||
console.log("kanker koek")
|
||||
|
||||
if(!cookieAuthSanitized) return {}
|
||||
|
||||
const kd = JSON.parse(cookieAuthSanitized);
|
||||
if(!kd.id || !kd.token || !kd.user_id) return {};
|
||||
|
||||
const foundAuth = await Auth.findOne({where: { id: kd.id},include:{model:User}});
|
||||
if(!foundAuth) return {};
|
||||
const authObject:AuthProps = {
|
||||
auth: {
|
||||
id:kd.id,
|
||||
token:kd.token,
|
||||
user_id:kd.user_id
|
||||
},
|
||||
user: await foundAuth.user
|
||||
}
|
||||
return authObject;
|
||||
const foundAuth = await Auth.findOne({
|
||||
where: { id: kd.id },
|
||||
include: { model: User },
|
||||
});
|
||||
if (!foundAuth) return {};
|
||||
const authObject: AuthContextProviderProps = {
|
||||
auth: {
|
||||
id: kd.id,
|
||||
token: kd.token,
|
||||
user_id: kd.user_id,
|
||||
},
|
||||
user: await foundAuth.user,
|
||||
};
|
||||
return authObject;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import ReactDOM from "react";
|
||||
import "/public/global.css"
|
||||
import "./index.css"
|
||||
import { Post } from "@/models";
|
||||
import { constructAPIUrl } from "@/util";
|
||||
import { constructAPIUrl } from "@/util/url";
|
||||
import Link from "next/link";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
|
||||
@ -17,7 +17,7 @@ type DeepPartial<T> = T extends object ? {
|
||||
|
||||
export default async function Test() {
|
||||
|
||||
const response = await fetch(constructAPIUrl('post'));
|
||||
const response = await fetch(await constructAPIUrl('post'));
|
||||
|
||||
const articles:Array<Attributes<Post>> = await response.json();
|
||||
|
||||
|
||||
14
src/components/client/SwaggerComponent.tsx
Normal file
14
src/components/client/SwaggerComponent.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import SwaggerUI from 'swagger-ui-react';
|
||||
import 'swagger-ui-react/swagger-ui.css';
|
||||
|
||||
type Props = {
|
||||
spec: Record<string, any>,
|
||||
};
|
||||
|
||||
function ReactSwagger({ spec }: Props) {
|
||||
return <SwaggerUI spec={spec} />;
|
||||
}
|
||||
|
||||
export default ReactSwagger;
|
||||
15
src/components/client/admin/CAuthHandler.tsx
Normal file
15
src/components/client/admin/CAuthHandler.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
'use client'
|
||||
import { AuthContext, AuthContextProviderProps } from "@/providers";
|
||||
import { ReactNode } from "react"
|
||||
|
||||
type CAuthProviderProps = {
|
||||
children?:ReactNode;
|
||||
authProps:AuthContextProviderProps
|
||||
}
|
||||
|
||||
export default function CAuthProvider(props:CAuthProviderProps){
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={props.authProps}>{props.children}</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
@ -1,19 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { serverAttemptAuthenticateUser } from "@/app/lib/actions/actions";
|
||||
import { AuthContext } from "@/providers/providers";
|
||||
import { constructAPIUrl } from "@/util/url";
|
||||
import { createContext, useState } from "react";
|
||||
import { useFormState, useFormStatus } from "react-dom";
|
||||
|
||||
|
||||
// async function authenticate(){
|
||||
// const url = await constructAPIUrl("auth");
|
||||
// const auth = await fetch(url);
|
||||
// }
|
||||
|
||||
|
||||
export default function LoginForm(){
|
||||
export default function CLoginForm(){
|
||||
const [loginResult, dispatch] = useFormState(serverAttemptAuthenticateUser, undefined);
|
||||
|
||||
return (
|
||||
@ -6,9 +6,6 @@ import { EntityEditorTextArea } from '../input/EntityEditorTextArea';
|
||||
import {
|
||||
ChangeEventHandler,
|
||||
MouseEventHandler,
|
||||
MutableRefObject,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
} from "@/app/lib/actions/entityManagement/post/postActions";
|
||||
|
||||
import { PostViewProps } from "@/views/admin/post";
|
||||
import { aifa, StateHook } from "@/util/";
|
||||
import { StateHook } from "@/util/";
|
||||
|
||||
export type PostTableStateProps = {
|
||||
posts: StateHook<GetPostsAttributes[]>;
|
||||
@ -110,13 +110,10 @@ export default function PostTable({
|
||||
: `${post.content.substring(0, 255)}...`}
|
||||
</td>
|
||||
<td key="project">
|
||||
{aifa(
|
||||
projects.find((e) => {
|
||||
console.log(e.id);
|
||||
return e.id == post.project.id;
|
||||
})?.readableIdentifier,
|
||||
"uncategorized"
|
||||
)}
|
||||
{projects.find((e) => {
|
||||
console.log(e.id);
|
||||
return e.id == post.project.id;
|
||||
})?.readableIdentifier || "uncategorized"}
|
||||
</td>
|
||||
<td key="createdAt">{post.createdAt?.toString()}</td>
|
||||
<td key="updatedAt">{post.updatedAt?.toString()}</td>
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
'use client'
|
||||
import { AuthContext, AuthProps } from "@/providers/providers";
|
||||
import { ReactNode, createContext } from "react"
|
||||
|
||||
type Props = {
|
||||
children?:ReactNode;
|
||||
authProps:AuthProps
|
||||
}
|
||||
|
||||
|
||||
export default function ClientAuthHandler(props:Props){
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={props.authProps}>{props.children}</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
@ -5,7 +5,6 @@ import {
|
||||
MutableRefObject,
|
||||
useLayoutEffect,
|
||||
ChangeEventHandler,
|
||||
useState,
|
||||
} from "react";
|
||||
import { StateHook } from "@/util";
|
||||
|
||||
|
||||
@ -1,43 +1,46 @@
|
||||
'use server'
|
||||
"use server";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
import LoginForm from "@/components/client/admin/loginForm";
|
||||
import ClientAuthHandler from "@/components/client/admin/clientAuthHandler";
|
||||
import CLoginForm from "@/components/client/admin/CLoginForm";
|
||||
import ClientAuthHandler from "@/components/client/admin/CAuthHandler";
|
||||
|
||||
import { serverValidateSessionCookie } from "@/app/lib/actions/actions";
|
||||
import { ReactNode } from "react";
|
||||
import { AuthContext, AuthProps } from "@/providers/providers";
|
||||
|
||||
import { AuthContextProviderProps } from "@/providers";
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
params?: any;
|
||||
requiredRole?: number;
|
||||
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 = {
|
||||
};
|
||||
if(koek){
|
||||
const kd = JSON.parse(koek);
|
||||
if(kd.id && kd.token && kd.user_id){
|
||||
p = {
|
||||
auth: {
|
||||
id:kd.id,
|
||||
token:kd.token,
|
||||
user_id:kd.user_id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const protoKoek = await cookies().get("auth")?.value;
|
||||
const koek = decodeURIComponent(protoKoek ? protoKoek : "");
|
||||
// console.log("koekje:" + koek)
|
||||
let p: AuthContextProviderProps = {};
|
||||
if (koek) {
|
||||
const kd = JSON.parse(koek);
|
||||
if (kd.id && kd.token && kd.user_id) {
|
||||
p = {
|
||||
auth: {
|
||||
id: kd.id,
|
||||
token: kd.token,
|
||||
user_id: kd.user_id,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row w-[100%] h-[100%]">
|
||||
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}>{props.children}</ClientAuthHandler>}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-row w-[100%] h-[100%]">
|
||||
{!(koek && (await serverValidateSessionCookie(koek))) ? (
|
||||
<CLoginForm>{}</CLoginForm>
|
||||
) : (
|
||||
<ClientAuthHandler authProps={p}>
|
||||
{props.children}
|
||||
</ClientAuthHandler>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
'use client'
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function Bootstrap(Component, children, pageProps){
|
||||
useEffect(() => {
|
||||
typeof document !== undefined ? require('bootstrap/dist/js/bootstrap') : null
|
||||
}, []);
|
||||
return <><div {...pageProps} /></>;
|
||||
}
|
||||
@ -8,7 +8,7 @@ import Link from "next/link";
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Router } from "next/router";
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { truncateString } from "@/util/utils";
|
||||
import { truncateString } from "@/util/strings";
|
||||
// @ts-ignore
|
||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||
import { ExampleComponent } from "./article";
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
margin-left:0px;
|
||||
padding: 1px 5px 1px 5px;
|
||||
border-radius: 20%;
|
||||
*display: inline;
|
||||
display: inline;
|
||||
}
|
||||
.tagbar{
|
||||
margin: 0px 0px 0px 0px;
|
||||
|
||||
@ -38,16 +38,14 @@ export function CPostView({
|
||||
posts: parseStateHook(useState(posts)),
|
||||
editor: parseStateHook(useState(initEditorState)),
|
||||
};
|
||||
|
||||
// render out the post table
|
||||
return (
|
||||
<>
|
||||
<PostTable
|
||||
headings={headings}
|
||||
posts={posts}
|
||||
projects={projects}
|
||||
actions={actions}
|
||||
state={state}
|
||||
></PostTable>
|
||||
</>
|
||||
<PostTable
|
||||
headings={headings}
|
||||
posts={posts}
|
||||
projects={projects}
|
||||
actions={actions}
|
||||
state={state}
|
||||
></PostTable>
|
||||
);
|
||||
}
|
||||
|
||||
@ -30,14 +30,6 @@ export async function PostView(props: Props) {
|
||||
"UpdatedAt",
|
||||
];
|
||||
|
||||
const actions: PostServerActions = {
|
||||
deletePost: deletePost,
|
||||
getPosts: getPostsWithBucketsAndProject,
|
||||
getProjects: getProjects,
|
||||
savePost: updatePost,
|
||||
// uploadAttachment:tryCreateAttachment
|
||||
};
|
||||
|
||||
const posts: GetPostsAttributes[] | undefined = handleActionResult(
|
||||
await getPostsWithBucketsAndProject()
|
||||
);
|
||||
@ -52,7 +44,12 @@ export async function PostView(props: Props) {
|
||||
posts={posts ? posts : []}
|
||||
projects={projects}
|
||||
headings={headings}
|
||||
actions={actions}
|
||||
actions={{
|
||||
deletePost: deletePost,
|
||||
getPosts: getPostsWithBucketsAndProject,
|
||||
getProjects: getProjects,
|
||||
savePost: updatePost
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cache: "no-store";
|
||||
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { ReactNode } from "react";
|
||||
import EntityManagementTable from "../../../components/client/EntityManagementTable";
|
||||
import PostTable from "@/components/client/admin/PostTable";
|
||||
import {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
//@ts-ignore
|
||||
// @ts-ignore
|
||||
import { createSwaggerSpec } from "next-swagger-doc";
|
||||
|
||||
export const getApiDocs = async () => {
|
||||
|
||||
@ -10,10 +10,11 @@ import {
|
||||
HasOne,
|
||||
UpdatedAt,
|
||||
CreatedAt,
|
||||
Table,
|
||||
} from '@sequelize/core/decorators-legacy';
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
|
||||
@Table({tableName: "DBStates"})
|
||||
export class DBState extends Model<InferAttributes<DBState>, InferCreationAttributes<DBState>>{
|
||||
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
|
||||
@ -20,7 +20,7 @@ export class Project extends Model<
|
||||
> {
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@PrimaryKey
|
||||
@Unique
|
||||
// @Unique
|
||||
@AutoIncrement
|
||||
declare id: CreationOptional<number>;
|
||||
@Attribute(DataTypes.STRING)
|
||||
|
||||
@ -25,7 +25,7 @@ export * from './UserPerms';
|
||||
|
||||
export const sequelize = new Sequelize({
|
||||
dialect: SqliteDialect,
|
||||
storage: 'db.sqlite',
|
||||
storage: './db/db.sqlite',
|
||||
models: [Auth, Attachment, Bucket, DBState, Post, PostBucket, PostTag, Project, Tag, User, UserPerms],
|
||||
});
|
||||
|
||||
|
||||
14
src/providers/authprovider.tsx
Normal file
14
src/providers/authprovider.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Auth, User } from "@/models";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
import { Context, createContext } from "react";
|
||||
|
||||
|
||||
export type AuthContextProviderProps = {
|
||||
auth?: Attributes<Auth>
|
||||
user?: Attributes<User>
|
||||
}
|
||||
|
||||
let p: AuthContextProviderProps = {}
|
||||
|
||||
export const AuthContext: Context<AuthContextProviderProps> = createContext(p);
|
||||
|
||||
@ -3,23 +3,14 @@
|
||||
import { Auth, User } from "@/models";
|
||||
import { ReactNode, createContext } from "react";
|
||||
import { Attributes, InferAttributes } from "@sequelize/core";
|
||||
import { AuthContext } from "./authprovider";
|
||||
|
||||
|
||||
export type AuthProps = {
|
||||
auth?: Attributes<Auth>
|
||||
user?: Attributes<User>
|
||||
}
|
||||
|
||||
let p: AuthProps = {}
|
||||
|
||||
export const AuthContext = createContext(p);
|
||||
|
||||
interface Props {
|
||||
interface ProvidersProps {
|
||||
children?: ReactNode;
|
||||
params?: any;
|
||||
}
|
||||
|
||||
export default function Providers(props:Props){
|
||||
export default function ContextProviders(props:ProvidersProps){
|
||||
return (
|
||||
<AuthContext.Provider value={{}}>{props.children}</AuthContext.Provider>
|
||||
)
|
||||
2
src/providers/index.ts
Normal file
2
src/providers/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './authprovider';
|
||||
export * from './contextproviders';
|
||||
@ -1,10 +1,9 @@
|
||||
'server only';
|
||||
// import Auth from "./Auth";
|
||||
export function getAPIEnv() {
|
||||
export async function getAPIEnv() {
|
||||
return {
|
||||
'schema': process.env.API_SCHEMA,
|
||||
'host': process.env.API_HOST,
|
||||
'port': process.env.API_PORT,
|
||||
'basepath': process.env.API_BASEPATH
|
||||
'basepath': process.env.API_BASEPATH,
|
||||
};
|
||||
}
|
||||
@ -1,2 +1,4 @@
|
||||
export * from './error';
|
||||
export * from './getAPIEnv';
|
||||
export * from './runapiaction';
|
||||
export * from './user';
|
||||
|
||||
19
src/util/api/runapiaction.ts
Normal file
19
src/util/api/runapiaction.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NextApiResponse } from "next";
|
||||
import { APIError } from "./error";
|
||||
|
||||
export type APIRequest = (request:Request)=>Promise<Response>;
|
||||
|
||||
export async function runApiAction(c:APIRequest,request:Request){
|
||||
try{
|
||||
return await c(request);
|
||||
}
|
||||
catch(e){
|
||||
if (e instanceof APIError){
|
||||
return new Response(e.info.responseText,{status:e.info.status});
|
||||
console.log("responded");
|
||||
}
|
||||
else{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/util/cookies/index.ts
Normal file
2
src/util/cookies/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './Cookies';
|
||||
export * from './parseSetCookie';
|
||||
@ -1,10 +0,0 @@
|
||||
|
||||
class Gens{
|
||||
|
||||
public static loremipsum():String
|
||||
{
|
||||
return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas eros a imperdiet ultrices. Maecenas tincidunt tristique dolor, vitae dignissim ligula faucibus sit amet. Ut hendrerit elit eu elit molestie, vel consectetur leo accumsan. Phasellus ex mi, dignissim at aliquam at, rutrum eget mi. Curabitur pellentesque auctor nulla sed pulvinar. Maecenas scelerisque orci at sem finibus tincidunt. Mauris viverra pulvinar nibh. Etiam ornare purus leo, at cursus elit ornare nec. Suspendisse potenti. Sed nisl libero, sollicitudin vitae dignissim sit amet, laoreet sit amet odio. Duis rhoncus felis ut erat facilisis, vitae rutrum odio sollicitudin. Praesent et scelerisque eros. Praesent laoreet eu orci ut blandit. Morbi dapibus nibh urna, eget blandit quam aliquet vitae. Nulla quam metus, volutpat et vulputate vel, viverra sed diam.'
|
||||
}
|
||||
|
||||
}
|
||||
export default Gens;
|
||||
@ -1,10 +1,5 @@
|
||||
export * from './api';
|
||||
export * from './auth';
|
||||
export * from './Cookies';
|
||||
export * from './DeepPartial';
|
||||
export * from './gens';
|
||||
export * from './getAPIEnv';
|
||||
export * from './parseSetCookie';
|
||||
export * from './cookies';
|
||||
export * from './deeppartial';
|
||||
export * from './state';
|
||||
export * from './url';
|
||||
export * from './utils';
|
||||
export * from './strings';
|
||||
|
||||
2
src/util/strings/index.ts
Normal file
2
src/util/strings/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './loremipsum';
|
||||
export * from './truncateString';
|
||||
6
src/util/strings/loremipsum.ts
Normal file
6
src/util/strings/loremipsum.ts
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
export function loremipsum():String
|
||||
{
|
||||
return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas eros a imperdiet ultrices. Maecenas tincidunt tristique dolor, vitae dignissim ligula faucibus sit amet. Ut hendrerit elit eu elit molestie, vel consectetur leo accumsan. Phasellus ex mi, dignissim at aliquam at, rutrum eget mi. Curabitur pellentesque auctor nulla sed pulvinar. Maecenas scelerisque orci at sem finibus tincidunt. Mauris viverra pulvinar nibh. Etiam ornare purus leo, at cursus elit ornare nec. Suspendisse potenti. Sed nisl libero, sollicitudin vitae dignissim sit amet, laoreet sit amet odio. Duis rhoncus felis ut erat facilisis, vitae rutrum odio sollicitudin. Praesent et scelerisque eros. Praesent laoreet eu orci ut blandit. Morbi dapibus nibh urna, eget blandit quam aliquet vitae. Nulla quam metus, volutpat et vulputate vel, viverra sed diam.'
|
||||
}
|
||||
|
||||
7
src/util/strings/truncateString.ts
Normal file
7
src/util/strings/truncateString.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export function truncateString(str:string = '', num:number = 255) {
|
||||
if (str.length > num) {
|
||||
return str.slice(0, num) + "...";
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
'use server'
|
||||
|
||||
import { getAPIEnv } from "../getAPIEnv";
|
||||
import { getAPIEnv } from "../api/getAPIEnv";
|
||||
|
||||
export function constructAPIUrl(endpoint:string){
|
||||
const { schema, host, port, basepath } = getAPIEnv();
|
||||
export async function constructAPIUrl(endpoint:string){
|
||||
const { schema, host, port, basepath } = await getAPIEnv();
|
||||
return `${schema}://${host}:${port}/${basepath}/${endpoint}`
|
||||
}
|
||||
export function constructUrl(endpoint:string){
|
||||
const { schema, host, port, basepath } = getAPIEnv();
|
||||
export async function constructUrl(endpoint:string){
|
||||
const { schema, host, port, basepath } = await getAPIEnv();
|
||||
return `${schema}://${host}:${port}/${endpoint}`
|
||||
}1
|
||||
@ -1,14 +0,0 @@
|
||||
'server only'
|
||||
import { ReactNode } from "react";
|
||||
import Gens from "./gens";
|
||||
function truncateString(str:string = '', num:number = 255) {
|
||||
if (str.length > num) {
|
||||
return str.slice(0, num) + "...";
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { Gens, truncateString }
|
||||
export const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b);
|
||||
Loading…
x
Reference in New Issue
Block a user