Merge pull request 'dev' (#4) from dev into production
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 0s
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 0s
Reviewed-on: #4
This commit is contained in:
commit
800bce807e
@ -7,4 +7,5 @@ jobs:
|
|||||||
runs-on: linux
|
runs-on: linux
|
||||||
steps:
|
steps:
|
||||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||||
- run: echo "🎉 Test job completed! 🚀"
|
- run: ssh
|
||||||
|
- run: echo "🍏 This job's status is ${{ gitea.job_status }}."
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import BreadNavigator from "@/components/bread-navigator";
|
|||||||
import RenderContent from "@/components/editor/render-content";
|
import RenderContent from "@/components/editor/render-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { appRoutes } from "@/config";
|
import { appRoutes } from "@/config";
|
||||||
import { hasPermission, Role } from "@/lib/validation/permissions";
|
import { hasPermission, Role } from "@/lib/validation/permissions";
|
||||||
import { auth } from "@/server/auth";
|
import { auth } from "@/server/auth";
|
||||||
@ -20,8 +21,8 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|||||||
? hasPermission(session.user.role, Role.EDITOR)
|
? hasPermission(session.user.role, Role.EDITOR)
|
||||||
: false;
|
: false;
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="">
|
||||||
<div className="flex w-full items-center justify-between">
|
{/* <div className="flex w-full items-center justify-between">
|
||||||
<div className="flex w-full items-center gap-4">
|
<div className="flex w-full items-center gap-4">
|
||||||
<BreadNavigator
|
<BreadNavigator
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@ -39,6 +40,28 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<div className="flex w-full flex-col gap-4 xl:flex-row">
|
||||||
|
<div className="w-full space-y-4">
|
||||||
|
<h1 className="text-4xl font-bold">{article.title}</h1>
|
||||||
|
<div className="w-full">
|
||||||
|
{article.content && <RenderContent content={article.content} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator
|
||||||
|
orientation="vertical"
|
||||||
|
className="sticky top-0 hidden h-screen -translate-y-4 xl:block"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"top-4 h-max w-full max-w-xl space-y-4 xl:sticky xl:max-w-sm"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="relative min-h-96 w-full space-y-4 overflow-hidden rounded-md border bg-background p-4">
|
||||||
|
<p className="text-center text-muted-foreground">
|
||||||
|
Kommentare bald verfügbar
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
{isEditor && (
|
{isEditor && (
|
||||||
<div className="flex w-full items-center justify-end space-x-2">
|
<div className="flex w-full items-center justify-end space-x-2">
|
||||||
<Badge
|
<Badge
|
||||||
@ -60,8 +83,7 @@ async function Page({ params }: { params: Promise<{ slug: string }> }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl font-bold">{article.title}</h1>
|
</div>
|
||||||
{article.content && <RenderContent content={article.content} />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ async function Page({ params }: CategoryPageProps) {
|
|||||||
articles: true,
|
articles: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
console.log(category);
|
||||||
|
|
||||||
if (!category) return notFound();
|
if (!category) return notFound();
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ function UserForm({ server_user, cb }: { server_user: User; cb?: () => void }) {
|
|||||||
const form = useForm<z.infer<typeof userSchema>>({
|
const form = useForm<z.infer<typeof userSchema>>({
|
||||||
resolver: zodResolver(userSchema),
|
resolver: zodResolver(userSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
|
email: server_user.email ?? "",
|
||||||
name: server_user?.name ?? "",
|
name: server_user?.name ?? "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export default async function Home() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Alert>
|
<Alert>
|
||||||
<div className="flex gap-2">
|
<div className="flex flex-col gap-2 sm:flex-row">
|
||||||
<Icons.logo className="size-8" />
|
<Icons.logo className="size-8" />
|
||||||
<div>
|
<div>
|
||||||
<AlertTitle className="font-bold">
|
<AlertTitle className="font-bold">
|
||||||
@ -25,7 +25,7 @@ export default async function Home() {
|
|||||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit.
|
Lorem ipsum, dolor sit amet consectetur adipisicing elit.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
<Button asChild className="ml-auto">
|
<Button asChild className="sm:ml-auto">
|
||||||
<Link href={appRoutes.about}>
|
<Link href={appRoutes.about}>
|
||||||
Erfahre mehr über {appConfig.name}
|
Erfahre mehr über {appConfig.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -68,7 +68,7 @@ export default ({ server_article }: { server_article: Article }) => {
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="flex w-full flex-col-reverse gap-4 space-y-4 xl:flex-row"
|
className="flex w-full flex-col-reverse gap-4 xl:flex-row"
|
||||||
>
|
>
|
||||||
<div className="flex w-full max-w-3xl flex-col gap-2">
|
<div className="flex w-full max-w-3xl flex-col gap-2">
|
||||||
<FormField
|
<FormField
|
||||||
@ -112,12 +112,7 @@ export default ({ server_article }: { server_article: Article }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div className={"top-4 h-max w-full max-w-xl xl:sticky xl:max-w-md"}>
|
||||||
className={cn(
|
|
||||||
"top-4 h-max w-full max-w-xl xl:sticky xl:max-w-md",
|
|
||||||
// loading && "border-t-blue-600",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="relative w-full space-y-4 overflow-hidden rounded-md border bg-background p-4">
|
<div className="relative w-full space-y-4 overflow-hidden rounded-md border bg-background p-4">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@ -6,18 +6,31 @@ import { Icons } from "../icons";
|
|||||||
|
|
||||||
function CategorySelect(props: Partial<ComboboxProps>) {
|
function CategorySelect(props: Partial<ComboboxProps>) {
|
||||||
const { data: categories } = api.category.getMany.useQuery();
|
const { data: categories } = api.category.getMany.useQuery();
|
||||||
|
const initialValue = categories?.find(
|
||||||
|
(c) => c.id === props?.initialValue,
|
||||||
|
)?.slug;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox
|
||||||
{...(props as ComboboxProps)}
|
{...(props as ComboboxProps)}
|
||||||
|
initialValue={initialValue}
|
||||||
messageUi={{
|
messageUi={{
|
||||||
select: "Kategorie auswählen...",
|
select: "Kategorie auswählen...",
|
||||||
selectIcon: Icons.category,
|
selectIcon: Icons.category,
|
||||||
placeholder: "Kategorie suchen...",
|
placeholder: "Kategorie suchen...",
|
||||||
empty: "Keine Kategorien gefunden",
|
empty: "Keine Kategorien gefunden",
|
||||||
}}
|
}}
|
||||||
|
onSelect={(value) => {
|
||||||
|
const id = categories?.find((c) => {
|
||||||
|
return c.slug === value;
|
||||||
|
})?.id!;
|
||||||
|
props?.onSelect?.(id);
|
||||||
|
}}
|
||||||
data={
|
data={
|
||||||
categories?.map(({ name, slug }) => ({ label: name, value: slug })) ??
|
categories?.map(({ name, slug }) => ({
|
||||||
[]
|
label: name,
|
||||||
|
value: slug,
|
||||||
|
})) ?? []
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -58,22 +58,18 @@ const updatedImage = UpdatedImage.configure({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskList = TaskList.configure({
|
const taskList = TaskList.configure({});
|
||||||
HTMLAttributes: {
|
|
||||||
class: cx("not-prose pl-2 "),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const taskItem = TaskItem.configure({
|
const taskItem = TaskItem.configure({
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
class: cx("flex gap-2 items-start my-4"),
|
class: cx("flex gap-2 items-start "),
|
||||||
},
|
},
|
||||||
nested: true,
|
nested: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const horizontalRule = HorizontalRule.configure({
|
const horizontalRule = HorizontalRule.configure({
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
class: cx("mt-4 mb-6 border-t border-muted-foreground"),
|
class: cx("mt-4 mb-6 border border-border"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,6 +81,21 @@ const starterKit = StarterKit.configure({
|
|||||||
heading: {
|
heading: {
|
||||||
levels: [2, 3, 4, 5, 6],
|
levels: [2, 3, 4, 5, 6],
|
||||||
},
|
},
|
||||||
|
bulletList: {
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: cx("list-disc ml-4 px-2 "),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderedList: {
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: cx("list-decimal ml-4 px-2 "),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blockquote: {
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: cx("m-4 border-l-4 border-border pl-4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
gapcursor: false,
|
gapcursor: false,
|
||||||
horizontalRule: false,
|
horizontalRule: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,18 +17,38 @@
|
|||||||
margin: 0.75rem 0;
|
margin: 0.75rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap ul,
|
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
||||||
.tiptap ol {
|
@apply size-4 border border-border bg-muted;
|
||||||
margin-left: 1rem;
|
-webkit-appearance: none;
|
||||||
padding: 0.25rem;
|
appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transform: translateY(0.17rem);
|
||||||
|
display: grid;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
place-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap ul {
|
ul[data-type="taskList"] li > label input[type="checkbox"]::before {
|
||||||
list-style-type: disc;
|
content: "";
|
||||||
|
width: 0.65em;
|
||||||
|
height: 0.65em;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: 120ms transform ease-in-out;
|
||||||
|
box-shadow: inset 1em 1em;
|
||||||
|
transform-origin: center;
|
||||||
|
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
||||||
}
|
}
|
||||||
|
ul[data-type="taskList"] li > label input[type="checkbox"]:checked::before {
|
||||||
.tiptap ol {
|
transform: scale(1);
|
||||||
list-style-type: decimal;
|
}
|
||||||
|
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
||||||
|
@apply text-foreground/75;
|
||||||
|
text-decoration: line-through;
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Heading styles */
|
/* Heading styles */
|
||||||
@ -65,17 +85,9 @@
|
|||||||
font-size: 1.125rem; /* Equivalent to text-lg */
|
font-size: 1.125rem; /* Equivalent to text-lg */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap blockquote {
|
/* .tiptap blockquote {
|
||||||
@apply m-4 border-l-4 border-border pl-4;
|
@apply ;
|
||||||
/* border-left: 1rem solid var(--primary);
|
} */
|
||||||
padding-left: 1rem; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.tiptap hr {
|
|
||||||
@apply border-border;
|
|
||||||
/* border-color: var(--border); */
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background-color: #5abbf7;
|
background-color: #5abbf7;
|
||||||
@ -89,52 +101,6 @@ img {
|
|||||||
@apply rounded-md;
|
@apply rounded-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task List */
|
|
||||||
ul[data-type="taskList"] li {
|
|
||||||
@apply m-0 my-3 flex items-center;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul[data-type="taskList"] li p {
|
|
||||||
@apply m-0 my-0 flex items-center;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
|
||||||
@apply size-4 border border-border bg-muted;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: grid;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
place-content: center;
|
|
||||||
}
|
|
||||||
/* ul[data-type="taskList"] li > label input[type="checkbox"]:hover {
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
ul[data-type="taskList"] li > label input[type="checkbox"]:active {
|
|
||||||
background-color: #000;
|
|
||||||
} */
|
|
||||||
ul[data-type="taskList"] li > label input[type="checkbox"]::before {
|
|
||||||
content: "";
|
|
||||||
width: 0.65em;
|
|
||||||
height: 0.65em;
|
|
||||||
transform: scale(0);
|
|
||||||
transition: 120ms transform ease-in-out;
|
|
||||||
box-shadow: inset 1em 1em;
|
|
||||||
transform-origin: center;
|
|
||||||
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
|
||||||
}
|
|
||||||
ul[data-type="taskList"] li > label input[type="checkbox"]:checked::before {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
|
||||||
@apply text-foreground/75;
|
|
||||||
text-decoration: line-through;
|
|
||||||
text-decoration-thickness: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ProseMirror:not(.dragging) .ProseMirror-selectednode {
|
.ProseMirror:not(.dragging) .ProseMirror-selectednode {
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
background-color: var(--novel-highlight-blue);
|
background-color: var(--novel-highlight-blue);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { createCallerFactory, createTRPCRouter } from "@/server/api/trpc";
|
|||||||
import { usersRouter } from "./routers/users";
|
import { usersRouter } from "./routers/users";
|
||||||
import { authorRouter } from "./routers/author";
|
import { authorRouter } from "./routers/author";
|
||||||
import { appRouter as globalRouter } from "./routers/app";
|
import { appRouter as globalRouter } from "./routers/app";
|
||||||
import { authRouter } from "./routers/auth";
|
// import { authRouter } from "./routers/auth";
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
*
|
*
|
||||||
@ -16,7 +16,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
users: usersRouter,
|
users: usersRouter,
|
||||||
author: authorRouter,
|
author: authorRouter,
|
||||||
app: globalRouter,
|
app: globalRouter,
|
||||||
auth: authRouter,
|
// auth: authRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@ -162,10 +162,15 @@ export const articleRouter = createTRPCRouter({
|
|||||||
)
|
)
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const limit = input?.limit ?? 50;
|
const limit = input?.limit ?? 50;
|
||||||
return (await ctx.db.query.articles.findMany({
|
const where = and(
|
||||||
where: input?.categoryId
|
input?.categoryId
|
||||||
? eq(articles.categoryId, input.categoryId)
|
? eq(articles.categoryId, input.categoryId)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
eq(articles.published, true),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (await ctx.db.query.articles.findMany({
|
||||||
|
where,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
columns: {
|
columns: {
|
||||||
title: true,
|
title: true,
|
||||||
|
|||||||
@ -1,54 +1,54 @@
|
|||||||
import { passwordSchema, userSchema } from "@/lib/validation/zod/user";
|
// import { passwordSchema, userSchema } from "@/lib/validation/zod/user";
|
||||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
// import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||||
import { z } from "zod";
|
// import { z } from "zod";
|
||||||
import { eq } from "drizzle-orm";
|
// import { eq } from "drizzle-orm";
|
||||||
import { users } from "@/server/db/schema";
|
// import { users } from "@/server/db/schema";
|
||||||
import argon from "argon2";
|
// import argon from "argon2";
|
||||||
|
|
||||||
export const authRouter = createTRPCRouter({
|
// export const authRouter = createTRPCRouter({
|
||||||
register: publicProcedure
|
// register: publicProcedure
|
||||||
.input(
|
// .input(
|
||||||
z.object({
|
// z.object({
|
||||||
user: userSchema,
|
// user: userSchema,
|
||||||
password: passwordSchema,
|
// password: passwordSchema,
|
||||||
}),
|
// }),
|
||||||
)
|
// )
|
||||||
.mutation(async ({ ctx, input }) => {
|
// .mutation(async ({ ctx, input }) => {
|
||||||
const {
|
// const {
|
||||||
password,
|
// password,
|
||||||
user: { email, name },
|
// user: { email, name },
|
||||||
} = input;
|
// } = input;
|
||||||
// Check if user already exists
|
// // Check if user already exists
|
||||||
try {
|
// try {
|
||||||
const existingUser = await ctx.db.query.users.findFirst({
|
// const existingUser = await ctx.db.query.users.findFirst({
|
||||||
where: eq(users.email, email),
|
// where: eq(users.email, email),
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (existingUser) {
|
// if (existingUser) {
|
||||||
return { success: false, message: "User already exists" };
|
// return { success: false, message: "User already exists" };
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Hash the password (12 is a good cost factor)
|
// // Hash the password (12 is a good cost factor)
|
||||||
const hashedPassword = await argon.hash(password);
|
// const hashedPassword = await argon.hash(password);
|
||||||
|
|
||||||
// Create user in database
|
// // Create user in database
|
||||||
const [user] = await ctx.db
|
// const [user] = await ctx.db
|
||||||
.insert(users)
|
// .insert(users)
|
||||||
.values({
|
// .values({
|
||||||
name,
|
// name,
|
||||||
email,
|
// email,
|
||||||
password: hashedPassword,
|
// password: hashedPassword,
|
||||||
})
|
// })
|
||||||
.returning({ id: users.id });
|
// .returning({ id: users.id });
|
||||||
console.log(user);
|
// console.log(user);
|
||||||
|
|
||||||
if (user) {
|
// if (user) {
|
||||||
return { success: true, message: "User created successfully" };
|
// return { success: true, message: "User created successfully" };
|
||||||
}
|
// }
|
||||||
return { success: false, message: "Error creating user" };
|
// return { success: false, message: "Error creating user" };
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.error(e);
|
// console.error(e);
|
||||||
return { success: false, message: "Error creating user" };
|
// return { success: false, message: "Error creating user" };
|
||||||
}
|
// }
|
||||||
}),
|
// }),
|
||||||
});
|
// });
|
||||||
|
|||||||
@ -85,6 +85,7 @@ export const categoryRouter = createTRPCRouter({
|
|||||||
name: true,
|
name: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
|
id: true,
|
||||||
},
|
},
|
||||||
})) as Category[];
|
})) as Category[];
|
||||||
}),
|
}),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user