From fbc97368314796862b5d5ec20c06b94cd090a841 Mon Sep 17 00:00:00 2001 From: Lumither Tao Date: Thu, 1 Aug 2024 01:39:24 +0800 Subject: [PATCH] feat(global) containerize --- .gitignore | 2 +- Makefile | 6 ++ backend/.dockerignore | 1 + backend/src/api/post/refresh.rs | 2 +- backend/src/main.rs | 32 +++---- container/dev/backend.containerfile | 9 ++ container/dev/frontend.containerfile | 14 ++++ container/release/backend.containerfile | 0 container/release/frontend.containerfile | 0 dev.compose.yaml | 34 ++++++++ frontend/.dockerignore | 10 +++ frontend/app/blog/Error.tsx | 1 + frontend/app/blog/api.ts | 27 ++++++ frontend/app/blog/fetch_posts_list.ts | 10 --- frontend/app/blog/page.tsx | 14 ++-- frontend/app/blog/post/[id]/Footer.tsx | 1 - frontend/app/blog/post/[id]/PostCard.tsx | 102 +++++++++++------------ frontend/next.config.mjs | 3 +- frontend/package.json | 1 - refresh.sh | 2 +- 20 files changed, 180 insertions(+), 91 deletions(-) create mode 100644 Makefile create mode 100644 backend/.dockerignore create mode 100644 container/dev/backend.containerfile create mode 100644 container/dev/frontend.containerfile create mode 100644 container/release/backend.containerfile create mode 100644 container/release/frontend.containerfile create mode 100644 dev.compose.yaml create mode 100644 frontend/.dockerignore create mode 100644 frontend/app/blog/Error.tsx create mode 100644 frontend/app/blog/api.ts delete mode 100644 frontend/app/blog/fetch_posts_list.ts diff --git a/.gitignore b/.gitignore index 828481a..7a62a31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # confidential -.env.local +.env.* # idea related .idea diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b837df5 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +ddev: + docker-compose -f dev.compose.yaml up --build + +dclean: + docker-compose -f dev.compose.yaml rm -fsv + diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1 @@ +/target diff --git a/backend/src/api/post/refresh.rs b/backend/src/api/post/refresh.rs index aacf0b7..dae22f7 100644 --- a/backend/src/api/post/refresh.rs +++ b/backend/src/api/post/refresh.rs @@ -44,7 +44,7 @@ pub async fn refresh( }; if is_force_refresh { - if let Err(e) = query("DROP TABLE hash, meta, post, tag;") + if let Err(e) = query("DROP TABLE IF EXISTS hash, meta, post, tag;") .execute(&db_connection) .await { diff --git a/backend/src/main.rs b/backend/src/main.rs index 17550e5..fa2f24a 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -4,12 +4,12 @@ use std::num::ParseIntError; use std::process::exit; use std::str::FromStr; -use axum::routing::{get, post}; use axum::Router; +use axum::routing::{get, post}; use dotenv::dotenv; -use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; use sqlx::ConnectOptions; -use tracing::{error, warn}; +use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use tracing::{error, info, warn}; use crate::api::get::get_post::get_post; use crate::api::get::get_post_list::get_post_list; @@ -25,10 +25,10 @@ mod model; async fn main() -> Result<(), Box> { match dotenv() { Ok(_) => { - println!("[Info] env loaded, starting up...") + println!("[Info] env loaded from .env file, starting up...") } - Err(e) => { - panic!("[Fatal] failed to load env var: {}", e) + Err(_) => { + println!("[Info] .env not detected, starting up...") } }; @@ -42,24 +42,24 @@ async fn main() -> Result<(), Box> { Err(_) => 3001, }; - let db_uri: String = env::var("DB_CONNECTION").unwrap_or_else(|e| { - error!( - "Invalid or missing env var: DB_CONNECTION: {}", - e.to_string() - ); + let db_uri: String = env::var("DB_URI").unwrap_or_else(|e| { + error!("Invalid or missing env var: DB_URI: {}", e.to_string()); exit(1); }); let db_connect_option = PgConnectOptions::from_str(&db_uri) .unwrap() .disable_statement_logging(); - let db_pool = PgPoolOptions::new() - .connect_with(db_connect_option) - .await - .unwrap_or_else(|e| { + let db_pool = match PgPoolOptions::new().connect_with(db_connect_option).await { + Ok(pool) => { + info!("Database connected"); + pool + } + Err(e) => { error!("Failed to load database: {}", e.to_string()); exit(1); - }); + } + }; let app = Router::new() .route("/refresh_posts", post(refresh)) diff --git a/container/dev/backend.containerfile b/container/dev/backend.containerfile new file mode 100644 index 0000000..76a0ea2 --- /dev/null +++ b/container/dev/backend.containerfile @@ -0,0 +1,9 @@ +FROM rust:latest + +WORKDIR /app + +COPY . . + +RUN cargo build + +CMD cargo r \ No newline at end of file diff --git a/container/dev/frontend.containerfile b/container/dev/frontend.containerfile new file mode 100644 index 0000000..fa74be7 --- /dev/null +++ b/container/dev/frontend.containerfile @@ -0,0 +1,14 @@ +# docker/dev.Dockerfile +FROM oven/bun:latest + +WORKDIR /app + +COPY package.json ./ +COPY bun.lockb ./ + +RUN bun install + +ENV NEXT_TELEMETRY_DISABLED 1 + +CMD bun run dev + diff --git a/container/release/backend.containerfile b/container/release/backend.containerfile new file mode 100644 index 0000000..e69de29 diff --git a/container/release/frontend.containerfile b/container/release/frontend.containerfile new file mode 100644 index 0000000..e69de29 diff --git a/dev.compose.yaml b/dev.compose.yaml new file mode 100644 index 0000000..96d42e4 --- /dev/null +++ b/dev.compose.yaml @@ -0,0 +1,34 @@ +services: + frontend: + build: + context: ./frontend + dockerfile: ../container/dev/frontend.containerfile + restart: always + ports: + - 3000:3000 + volumes: + - ./frontend:/app:ro + - /app/node_modules + - /app/.next + env_file: + - .env.dev + + + backend: + build: + context: ./backend + dockerfile: ../container/dev/backend.containerfile + restart: always + ports: + - 3001:3001 + volumes: + - ./blog_posts_git:/app/blog_posts_git:ro + env_file: + - .env.dev + + postgres: + image: postgres:16.3 + ports: + - 5432:5432 + env_file: + - .env.dev diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..88c64d6 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,10 @@ +/node_modules + +/.next/ +/out/ +/build + +.DS_Store +*.pem + +.env*.local \ No newline at end of file diff --git a/frontend/app/blog/Error.tsx b/frontend/app/blog/Error.tsx new file mode 100644 index 0000000..98d1a4c --- /dev/null +++ b/frontend/app/blog/Error.tsx @@ -0,0 +1 @@ +// todo: create component diff --git a/frontend/app/blog/api.ts b/frontend/app/blog/api.ts new file mode 100644 index 0000000..307316a --- /dev/null +++ b/frontend/app/blog/api.ts @@ -0,0 +1,27 @@ +const LOCAL_ENDPOINT = process.env.API_LOCAL_URL; +const REMOTE_ENDPOINT = process.env.API_REMOTE_URL; + +const NOCTURNE_ENDPOINT = ((isServerSide = true) => { + if (process.env.NODE_ENV === 'production') { + if (isServerSide) { + return LOCAL_ENDPOINT; + } else { + return REMOTE_ENDPOINT; + } + } else { + return LOCAL_ENDPOINT; + } +}); + +export async function fetchNocturneApi(path: string, isServerSideRequest = true): Promise<{ code: number, ret: any }> { + let res = await fetch(`${ NOCTURNE_ENDPOINT(isServerSideRequest) }${ path }`, { + cache: 'no-cache' + }); + + if (!res.ok) { + return { code: res.status, ret: (await res.json()) }; + } + + return { code: res.status, ret: await res.json() }; +} + diff --git a/frontend/app/blog/fetch_posts_list.ts b/frontend/app/blog/fetch_posts_list.ts deleted file mode 100644 index 855b2cc..0000000 --- a/frontend/app/blog/fetch_posts_list.ts +++ /dev/null @@ -1,10 +0,0 @@ -import axios from 'axios'; - -export default async function fetch_posts_list(): Promise { - const request_url = `${ process.env.BACKEND_URL }/get_post_list`; - const response = await axios(request_url); - if (response.status !== 200) { - throw new Error(response.statusText); - } - return response.data; -} diff --git a/frontend/app/blog/page.tsx b/frontend/app/blog/page.tsx index 3f37e6f..b2fe73c 100644 --- a/frontend/app/blog/page.tsx +++ b/frontend/app/blog/page.tsx @@ -1,14 +1,14 @@ import React from 'react'; -import fetch_posts_list from '@/app/blog/fetch_posts_list'; import BlogPostCard from '@/app/blog/BlogPostCard'; import { Card, CardBody } from '@nextui-org/card'; import { MotionDiv } from '@/app/public/MotionDiv'; +import { fetchNocturneApi } from '@/app/blog/api'; async function Blog() { - try { - const res = await fetch_posts_list(); - let posts = res['posts']; + const { code, ret } = await fetchNocturneApi('/get_post_list'); + if (code == 200) { + let posts = ret['posts']; return ( // blog list
    @@ -25,15 +25,15 @@ async function Blog() { ) }
); - } catch (e: any) { + } else { return ( + // todo: use Error.tsx - Fatal: { e.toString() } + Fatal: { ret } ); } - } export default Blog; diff --git a/frontend/app/blog/post/[id]/Footer.tsx b/frontend/app/blog/post/[id]/Footer.tsx index 38b42b6..ecc02d5 100644 --- a/frontend/app/blog/post/[id]/Footer.tsx +++ b/frontend/app/blog/post/[id]/Footer.tsx @@ -10,7 +10,6 @@ type Props = { const Footer = (props: Props) => { const { post } = props; - const header_img = post['header_img']; const adjacent_posts = post['adj']; const prev_post = adjacent_posts['prev']; const next_post = adjacent_posts['next']; diff --git a/frontend/app/blog/post/[id]/PostCard.tsx b/frontend/app/blog/post/[id]/PostCard.tsx index c6dbbd3..b64b94d 100644 --- a/frontend/app/blog/post/[id]/PostCard.tsx +++ b/frontend/app/blog/post/[id]/PostCard.tsx @@ -4,60 +4,48 @@ import { Card, CardBody } from '@nextui-org/card'; import remarkGfm from 'remark-gfm'; import remarkToc from 'remark-toc'; import remarkFrontmatter from 'remark-frontmatter'; -import axios from 'axios'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; import rehypeRaw from 'rehype-raw'; import rehypeSanitize from 'rehype-sanitize'; import Headers from '@/app/blog/post/[id]/Headers'; import Footer from '@/app/blog/post/[id]/Footer'; +import { fetchNocturneApi } from '@/app/blog/api'; type Props = { id: string } async function PostCard(props: Props) { + + const { code, ret } = await fetchNocturneApi(`/get_post/${ props.id }`); + + if (code == 200) { - let response: any; - try { - response = await axios(`${ process.env.BACKEND_URL }/get_post/${ props.id }`); - } catch (e) { return ( - - - Fatal: Failed to load the post. - - - ); - } +
+ + {/* card headers */ } + - let post_data = response.data; - - return ( -
- - - {/* card headers */ } - - - {/* card body */ } - - + + remarkPlugins={ [ remarkGfm, remarkToc, remarkFrontmatter ] } + rehypePlugins={ [ rehypeRaw, rehypeSanitize ] } + components={ { + pre(props) { + const { node, className, children, ...rest } = props; + if ((children as any)['type'] === 'code') { + const match = /language-(\w+)/.exec((children as any)['props']['className'] || ''); + let lang = match ? match[1] : 'text'; + return ( +
                                             
                                         
- ); - } else { - return ( -
+                                        );
+                                    } else {
+                                        return (
+                                            
                                             
                                                 { children }
                                             
                                         
- ); + ); + } } - } - } } - >{ (post_data as any)['content'] } + } } + >{ ret['content'] } + + + + {/* card footer */ } + +
+ + +
+ ); + } else { + return
+ + + Fatal: { ret } - - - {/* card footer */ } - -
- ); +
; + } + } export default PostCard; diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 2de3023..c98ab2c 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -11,7 +11,8 @@ const nextConfig = { hostname: 'pic.re' } ] - } + }, + output: 'standalone' }; export default nextConfig; diff --git a/frontend/package.json b/frontend/package.json index 3b8a622..580b7b0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,6 @@ "dependencies": { "@nextui-org/react": "^2.3.5", "@types/react-syntax-highlighter": "^15.5.13", - "axios": "^1.6.8", "framer-motion": "^11.2.10", "lxgw-wenkai-webfont": "^1.7.0", "next": "14.2.3", diff --git a/refresh.sh b/refresh.sh index 7a3f76f..f2fa921 100755 --- a/refresh.sh +++ b/refresh.sh @@ -28,7 +28,7 @@ check_for_updates() { --url "localhost:$BACKEND_PORT" #todo: to be continued fi - } +} if [ -d "$GIT_WORK_DIR" ]; then check_for_updates