mirror of
https://github.com/DerTyp7/explainegy-nextjs.git
synced 2025-10-29 04:42:12 +01:00
add prisma
This commit is contained in:
7
.env
Normal file
7
.env
Normal file
@@ -0,0 +1,7 @@
|
||||
# Environment variables declared in this file are automatically made available to Prisma.
|
||||
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
|
||||
|
||||
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres?schema=explainegy"
|
||||
@@ -1,18 +1,19 @@
|
||||
import React from "react";
|
||||
import { TutorialMeta } from "./page";
|
||||
import prisma from "../../../lib/prisma";
|
||||
import styles from "../../../styles/TutorialContentTable.module.scss";
|
||||
import { Article, ContentTableEntry } from "@prisma/client";
|
||||
|
||||
export default function ContentTable({
|
||||
tutorialMeta,
|
||||
contentTableEntries,
|
||||
}: {
|
||||
tutorialMeta: TutorialMeta;
|
||||
contentTableEntries: ContentTableEntry[];
|
||||
}) {
|
||||
return (
|
||||
<div className={styles.tutorialContentTable}>
|
||||
<div className={styles.stickyContainer}>
|
||||
<div className={styles.list}>
|
||||
<h2>Contents</h2>
|
||||
{tutorialMeta?.contentTable?.map((e, i) => {
|
||||
{contentTableEntries?.map((e, i) => {
|
||||
return (
|
||||
<a key={i} href={"#" + e.anchor}>
|
||||
{e.title}
|
||||
@@ -20,7 +21,7 @@ export default function ContentTable({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{tutorialMeta?.contentTable?.length < 15 ? (
|
||||
{contentTableEntries?.length < 15 ? (
|
||||
<div className={styles.adContainer}>Future advertisement</div>
|
||||
) : (
|
||||
""
|
||||
17
app/tutorials/[articleName]/head.tsx
Normal file
17
app/tutorials/[articleName]/head.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Article } from "@prisma/client";
|
||||
import { GetArticle } from "./page";
|
||||
|
||||
export default async function Head({
|
||||
params,
|
||||
}: {
|
||||
params: { articleName: string };
|
||||
}) {
|
||||
const articleName: string = params.articleName;
|
||||
const article: Article = await GetArticle(articleName);
|
||||
return (
|
||||
<>
|
||||
<title>{article.title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
73
app/tutorials/[articleName]/page.tsx
Normal file
73
app/tutorials/[articleName]/page.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { marked } from "marked";
|
||||
import ContentTable from "./ContentTable";
|
||||
import Sidebar from "./Sidebar";
|
||||
import styles from "../../../styles/Tutorial.module.scss";
|
||||
import LoadMarkdown from "./LoadMarkdown";
|
||||
import prisma from "../../../lib/prisma";
|
||||
import { Article, ContentTableEntry } from "@prisma/client";
|
||||
|
||||
export async function GetContentTableEntries(
|
||||
article: Article
|
||||
): Promise<ContentTableEntry[]> {
|
||||
const entries = await prisma.contentTableEntry.findMany({
|
||||
where: { article: article },
|
||||
orderBy: { orderIndex: "asc" },
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export async function GetArticle(articleName: string) {
|
||||
const article = await prisma.article.findUnique({
|
||||
where: { name: articleName.toLowerCase() },
|
||||
});
|
||||
|
||||
return article;
|
||||
}
|
||||
|
||||
function ParseMarkdown(markdown: string): string {
|
||||
let result = marked.parse(markdown);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//* MAIN
|
||||
export default async function Tutorial({
|
||||
params,
|
||||
}: {
|
||||
params: { articleName: string };
|
||||
}) {
|
||||
const articleName: string = params.articleName;
|
||||
const article: Article = await GetArticle(articleName);
|
||||
const markdown: string = article.markdown;
|
||||
const contentTableEntries: ContentTableEntry[] = await GetContentTableEntries(
|
||||
article
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.tutorial}>
|
||||
<ContentTable contentTableEntries={contentTableEntries} />
|
||||
<div className={styles.tutorialContent}>
|
||||
<div className={styles.head}>
|
||||
<h1>{article.title}</h1>
|
||||
</div>
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: ParseMarkdown(markdown),
|
||||
}}
|
||||
></div>
|
||||
<LoadMarkdown />
|
||||
</div>
|
||||
<Sidebar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const articles = await prisma.article.findMany();
|
||||
|
||||
return articles.map((article) => ({
|
||||
articleName: article.name ?? "",
|
||||
}));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { GetTutorialMeta, TutorialMeta } from "./page";
|
||||
export default async function Head({
|
||||
params,
|
||||
}: {
|
||||
params: { tutorialId: string };
|
||||
}) {
|
||||
const tutorialId: string = params.tutorialId;
|
||||
const tutorialMeta: TutorialMeta = await GetTutorialMeta(tutorialId);
|
||||
return (
|
||||
<>
|
||||
<title>{tutorialMeta.title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { marked } from "marked";
|
||||
import { db, storage } from "../../../firebase-config";
|
||||
import { collection, doc, getDoc, getDocs } from "firebase/firestore";
|
||||
import { getDownloadURL, ref } from "firebase/storage";
|
||||
import ContentTable from "./ContentTable";
|
||||
import Sidebar from "./Sidebar";
|
||||
import styles from "../../../styles/Tutorial.module.scss";
|
||||
import LoadMarkdown from "./LoadMarkdown";
|
||||
import Head from "next/head";
|
||||
|
||||
export type ContentTable = {
|
||||
anchor: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type TutorialMeta = {
|
||||
id: string;
|
||||
title: string;
|
||||
contentTable: ContentTable[];
|
||||
};
|
||||
|
||||
export async function GetTutorialMeta(
|
||||
tutorialId: string
|
||||
): Promise<TutorialMeta> {
|
||||
const firebaseData = await getDoc(doc(db, "tutorials", tutorialId));
|
||||
const firebaseJsonData = firebaseData.data();
|
||||
|
||||
const tutorial: TutorialMeta = {
|
||||
id: tutorialId,
|
||||
title: firebaseJsonData?.title ?? "Tutorial not found!",
|
||||
contentTable: firebaseJsonData?.contentTable ?? [],
|
||||
};
|
||||
return tutorial;
|
||||
}
|
||||
|
||||
async function FetchTutorialMarkdown(tutorialId: string) {
|
||||
try {
|
||||
const url = await getDownloadURL(
|
||||
ref(storage, `markdowns/${tutorialId}.md`)
|
||||
);
|
||||
const data = await fetch(url, {
|
||||
next: { revalidate: 30 * 60 },
|
||||
});
|
||||
return await data.text();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function ParseMarkdown(markdown: string): string {
|
||||
let result = marked.parse(markdown);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//* MAIN
|
||||
export default async function Tutorial({
|
||||
params,
|
||||
}: {
|
||||
params: { tutorialId: string };
|
||||
}) {
|
||||
const tutorialId: string = params.tutorialId;
|
||||
const tutorialMeta: TutorialMeta = await GetTutorialMeta(tutorialId);
|
||||
const markdown: string = await FetchTutorialMarkdown(tutorialId);
|
||||
|
||||
return (
|
||||
<div className={styles.tutorial}>
|
||||
<ContentTable tutorialMeta={tutorialMeta} />
|
||||
<div className={styles.tutorialContent}>
|
||||
<div className={styles.head}>
|
||||
<h1>{tutorialMeta.title}</h1>
|
||||
</div>
|
||||
<div
|
||||
className="markdown"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: ParseMarkdown(markdown),
|
||||
}}
|
||||
></div>
|
||||
<LoadMarkdown />
|
||||
</div>
|
||||
<Sidebar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const data = await getDocs(collection(db, "tutorials"));
|
||||
|
||||
return data.docs.map((doc) => ({
|
||||
tutorialId: doc.id,
|
||||
}));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
image: postgres:latest
|
||||
container_name: explainegy_postgres
|
||||
volumes:
|
||||
- ./docker_data/postgres:/var/lib/postgresql/data
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
test: "exit 0"
|
||||
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: explainegy_pgadmin
|
||||
restart: always
|
||||
environment:
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
ports:
|
||||
- "5050:80"
|
||||
volumes:
|
||||
- ./docker/servers.json:/pgadmin4/servers.json # preconfigured servers/connections
|
||||
- ./docker/pgpass:/pgpass # passwords for the connections in this file
|
||||
- ./docker_data/servers.json:/pgadmin4/servers.json # preconfigured servers/connections
|
||||
- ./docker_data/pgpass:/pgpass # passwords for the connections in this file
|
||||
depends_on:
|
||||
- "postgres"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
docker_data/postgres/base/1/pg_internal.init
Normal file
BIN
docker_data/postgres/base/1/pg_internal.init
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
docker_data/postgres/base/5/16406
Normal file
BIN
docker_data/postgres/base/5/16406
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16410
Normal file
BIN
docker_data/postgres/base/5/16410
Normal file
Binary file not shown.
0
docker_data/postgres/base/5/16413
Normal file
0
docker_data/postgres/base/5/16413
Normal file
BIN
docker_data/postgres/base/5/16414
Normal file
BIN
docker_data/postgres/base/5/16414
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16415
Normal file
BIN
docker_data/postgres/base/5/16415
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16416
Normal file
BIN
docker_data/postgres/base/5/16416
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16417
Normal file
BIN
docker_data/postgres/base/5/16417
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16420
Normal file
BIN
docker_data/postgres/base/5/16420
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16421
Normal file
BIN
docker_data/postgres/base/5/16421
Normal file
Binary file not shown.
0
docker_data/postgres/base/5/16427
Normal file
0
docker_data/postgres/base/5/16427
Normal file
BIN
docker_data/postgres/base/5/16428
Normal file
BIN
docker_data/postgres/base/5/16428
Normal file
Binary file not shown.
BIN
docker_data/postgres/base/5/16429
Normal file
BIN
docker_data/postgres/base/5/16429
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
docker_data/postgres/global/pg_internal.init
Normal file
BIN
docker_data/postgres/global/pg_internal.init
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
1
|
||||
/var/lib/postgresql/data
|
||||
1671907198
|
||||
1672100396
|
||||
5432
|
||||
/var/run/postgresql
|
||||
*
|
||||
699789 5
|
||||
699789 0
|
||||
ready
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"Servers": {
|
||||
"1": {
|
||||
"Name": "trading_postgis",
|
||||
"Group": "trading_postgis_group",
|
||||
"Name": "explainegy_postgres",
|
||||
"Group": "explainegy_postgres_group",
|
||||
"Host": "host.docker.internal",
|
||||
"Port": 5432,
|
||||
"MaintenanceDB": "postgres",
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { getFirestore } from "@firebase/firestore";
|
||||
import { getStorage } from "firebase/storage";
|
||||
import { initializeApp } from "firebase/app";
|
||||
|
||||
const firebaseConfig = {
|
||||
apiKey: "AIzaSyB4U9wobUn7hpqt-gVuNQJPdhfpVdSGPIg",
|
||||
authDomain: "next-tutorials-7e130.firebaseapp.com",
|
||||
projectId: "next-tutorials-7e130",
|
||||
storageBucket: "next-tutorials-7e130.appspot.com",
|
||||
messagingSenderId: "500863392288",
|
||||
appId: "1:500863392288:web:4f2f667fef3a571b1d3a45",
|
||||
};
|
||||
|
||||
const app = initializeApp(firebaseConfig);
|
||||
|
||||
export const db = getFirestore();
|
||||
export const storage = getStorage(app);
|
||||
14
lib/prisma.ts
Normal file
14
lib/prisma.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
let prisma: PrismaClient;
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
prisma = new PrismaClient();
|
||||
} else {
|
||||
if (!global.prisma) {
|
||||
global.prisma = new PrismaClient();
|
||||
}
|
||||
prisma = global.prisma;
|
||||
}
|
||||
|
||||
export default prisma;
|
||||
2720
package-lock.json
generated
2720
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "explainegy",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -10,25 +10,31 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.0.7",
|
||||
"@prisma/client": "^4.8.0",
|
||||
"@types/marked": "^4.0.8",
|
||||
"@types/node": "18.11.17",
|
||||
"@types/pg-promise": "^5.4.3",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.9",
|
||||
"encoding": "^0.1.13",
|
||||
"eslint": "8.30.0",
|
||||
"eslint-config-next": "13.0.7",
|
||||
"firebase": "^9.15.0",
|
||||
"marked": "^4.2.4",
|
||||
"next": "^13.0.8-canary.0",
|
||||
"next": "^13.1.1-canary.1",
|
||||
"node-html-parser": "^6.1.4",
|
||||
"pg": "^8.8.0",
|
||||
"pg-promise": "^10.15.4",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "18.2.0",
|
||||
"react-code-blocks": "^0.0.9-0",
|
||||
"react-dom": "18.2.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sass": "^1.57.0",
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "4.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/prismjs": "^1.26.0"
|
||||
"@types/node": "^18.11.17",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"prisma": "^4.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
29
prisma/schema.prisma
Normal file
29
prisma/schema.prisma
Normal file
@@ -0,0 +1,29 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Article {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
title String @unique
|
||||
markdown String
|
||||
contentTableEntries ContentTableEntry[]
|
||||
dateCreated DateTime @default(now())
|
||||
dateUpdated DateTime @default(now())
|
||||
}
|
||||
|
||||
model ContentTableEntry {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
anchor String
|
||||
orderIndex Int
|
||||
articleId Int
|
||||
article Article @relation(fields: [articleId], references: [id])
|
||||
dateCreated DateTime @default(now())
|
||||
dateUpdated DateTime @default(now())
|
||||
}
|
||||
Reference in New Issue
Block a user