mirror of
https://github.com/Lumither/nocturne.git
synced 2026-10-17 10:26:21 +00:00
feat(global) containerize
This commit is contained in:
19 files changed
+145
-56
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
# confidential
|
||||
.env.local
|
||||
.env.*
|
||||
|
||||
# idea related
|
||||
.idea
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
ddev:
|
||||
docker-compose -f dev.compose.yaml up --build
|
||||
|
||||
dclean:
|
||||
docker-compose -f dev.compose.yaml rm -fsv
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
@@ -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
|
||||
{
|
||||
|
||||
+16
-16
@@ -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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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))
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM rust:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN cargo build
|
||||
|
||||
CMD cargo r
|
||||
@@ -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
|
||||
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
/node_modules
|
||||
|
||||
/.next/
|
||||
/out/
|
||||
/build
|
||||
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
.env*.local
|
||||
@@ -0,0 +1 @@
|
||||
// todo: create component
|
||||
@@ -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() };
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export default async function fetch_posts_list(): Promise<any> {
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
<ul className={ 'flex flex-col items-center w-full space-y-7' }>
|
||||
@@ -25,15 +25,15 @@ async function Blog() {
|
||||
) }
|
||||
</ul>
|
||||
);
|
||||
} catch (e: any) {
|
||||
} else {
|
||||
return (
|
||||
// todo: use Error.tsx
|
||||
<Card className={ `w-full` }>
|
||||
<CardBody>
|
||||
Fatal: { e.toString() }
|
||||
Fatal: { ret }
|
||||
</CardBody>
|
||||
</Card>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Blog;
|
||||
@@ -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'];
|
||||
|
||||
@@ -4,13 +4,13 @@ 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
|
||||
@@ -18,28 +18,16 @@ type Props = {
|
||||
|
||||
async function PostCard(props: Props) {
|
||||
|
||||
let response: any;
|
||||
try {
|
||||
response = await axios(`${ process.env.BACKEND_URL }/get_post/${ props.id }`);
|
||||
} catch (e) {
|
||||
return (
|
||||
<Card className={ `w-full` }>
|
||||
<CardBody>
|
||||
Fatal: Failed to load the post.
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
const { code, ret } = await fetchNocturneApi(`/get_post/${ props.id }`);
|
||||
|
||||
|
||||
let post_data = response.data;
|
||||
if (code == 200) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className={ `max-w-full relative` }>
|
||||
|
||||
{/* card headers */ }
|
||||
<Headers post={ post_data } />
|
||||
<Headers post={ ret } />
|
||||
|
||||
{/* card body */ }
|
||||
<CardBody>
|
||||
@@ -86,17 +74,27 @@ async function PostCard(props: Props) {
|
||||
}
|
||||
}
|
||||
} }
|
||||
>{ (post_data as any)['content'] }</Markdown>
|
||||
>{ ret['content'] }</Markdown>
|
||||
</CardBody>
|
||||
|
||||
|
||||
{/* card footer */ }
|
||||
|
||||
<Footer post={ post_data } />
|
||||
<Footer post={ ret } />
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <div>
|
||||
<Card className={ `w-full` }>
|
||||
<CardBody>
|
||||
Fatal: { ret }
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PostCard;
|
||||
@@ -11,7 +11,8 @@ const nextConfig = {
|
||||
hostname: 'pic.re'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
output: 'standalone'
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user