From 7cd8db36e2888abfd71fdad01c68562982753f43 Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 18 Sep 2024 17:31:06 +0900 Subject: [PATCH 1/4] feat: Add simple-git dependency --- package-lock.json | 35 ++++++++++++++++++++++++++++++++--- package.json | 3 ++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 262b8ea..1946875 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dotenv": "^16.4.5", "mdsvex": "^0.12.3", "pg": "^8.12.0", - "sass": "^1.77.8" + "sass": "^1.77.8", + "simple-git": "^3.26.0" }, "devDependencies": { "@playwright/test": "^1.28.1", @@ -299,6 +300,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1253,7 +1269,6 @@ "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -2408,7 +2423,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -3313,6 +3327,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-git": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.26.0.tgz", + "integrity": "sha512-5tbkCSzuskR6uA7uA23yjasmA0RzugVo8QM2bpsnxkrgP13eisFT7TMS4a+xKEJvbmr4qf+l0WT3eKa9IxxUyw==", + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", diff --git a/package.json b/package.json index 99a33da..827bdf0 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "dotenv": "^16.4.5", "mdsvex": "^0.12.3", "pg": "^8.12.0", - "sass": "^1.77.8" + "sass": "^1.77.8", + "simple-git": "^3.26.0" } } -- 2.43.0 From 7032c552a8d7076fb156b2938938e85208ebe1b5 Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 18 Sep 2024 17:32:02 +0900 Subject: [PATCH 2/4] feat: Refactor formatted_date component and add format function --- src/lib/components/formatted_date.svelte | 17 ++--------------- src/lib/scripts/formatted_date.ts | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 src/lib/scripts/formatted_date.ts diff --git a/src/lib/components/formatted_date.svelte b/src/lib/components/formatted_date.svelte index 2930e4b..d06fa8f 100644 --- a/src/lib/components/formatted_date.svelte +++ b/src/lib/components/formatted_date.svelte @@ -3,22 +3,9 @@ if (typeof date === 'string') date = new Date(date); - const pad = function (str: string): string { - return ('0' + str).slice(-2); - }; + import { format } from "$lib/scripts/formatted_date"; - const year = date.getFullYear().toString(); - const month = pad((date.getMonth() + 1).toString()); - const day = pad(date.getDate().toString()); - const hour = pad(date.getHours().toString()); - const min = pad(date.getMinutes().toString()); - const sec = pad(date.getSeconds().toString()); - const tz = -date.getTimezoneOffset(); - const sign = tz >= 0 ? '+' : '-'; - const tzHour = pad((tz / 60).toString()); - const tzMin = pad((tz % 60).toString()); - - let formattedDate = `${year}-${month}-${day}T${hour}:${min}:${sec}${sign}${tzHour}:${tzMin}`; + const formattedDate = format(date); {formattedDate} diff --git a/src/lib/scripts/formatted_date.ts b/src/lib/scripts/formatted_date.ts new file mode 100644 index 0000000..b8b2667 --- /dev/null +++ b/src/lib/scripts/formatted_date.ts @@ -0,0 +1,20 @@ +export const format = ( + date: Date +) => { + const pad = function (str: string): string { + return ('0' + str).slice(-2); + }; + + const year = date.getFullYear().toString(); + const month = pad((date.getMonth() + 1).toString()); + const day = pad(date.getDate().toString()); + const hour = pad(date.getHours().toString()); + const min = pad(date.getMinutes().toString()); + const sec = pad(date.getSeconds().toString()); + const tz = -date.getTimezoneOffset(); + const sign = tz >= 0 ? '+' : '-'; + const tzHour = pad((tz / 60).toString()); + const tzMin = pad((tz % 60).toString()); + + return `${year}-${month}-${day}T${hour}:${min}:${sec}${sign}${tzHour}:${tzMin}`; +}; \ No newline at end of file -- 2.43.0 From 3145073381459e06616903d47a2b7215778a7c47 Mon Sep 17 00:00:00 2001 From: Hare Date: Sat, 21 Sep 2024 14:51:15 +0900 Subject: [PATCH 3/4] feat: add a column to store datafrom git --- src/lib/server/database/init.ts | 137 ++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 58 deletions(-) diff --git a/src/lib/server/database/init.ts b/src/lib/server/database/init.ts index f57c908..be20422 100644 --- a/src/lib/server/database/init.ts +++ b/src/lib/server/database/init.ts @@ -1,30 +1,25 @@ // initialize import type { Postgres } from '$lib/server/database'; import fs from 'fs'; -import { execSync } from 'child_process'; import compile from '$lib/server/article'; +import article_git from '$lib/server/aritcle-git'; + +import { format } from '$lib/scripts/formatted_date'; export default async function init(db: Postgres) { - if (fs.existsSync('./articles/')) { - console.log('Pulling articles from git..'); - const stdout = execSync('git -c core.sshCommand="ssh -i ../key -F /dev/null" pull', { cwd: './articles/' }); - console.log(stdout.toString()); - } else { - console.log('Cloning articles from git..'); - const stdout = execSync('git -c core.sshCommand="ssh -i ./key -F /dev/null" clone git@gitea.hareworks.net:Hare/blog-articles.git articles', { cwd: './' }); - console.log(stdout.toString()); - } - - const schemas = [ + await cloneRepo(); + await createTable(db, [ { name: 'article', columns: [ { name: 'seq', type: 'serial', constraint: 'primary key' }, { name: 'id', type: 'text', constraint: 'not null' }, - { name: 'title', type: 'text', constraint: 'not null' }, - { name: 'category', type: 'text', constraint: 'not null' }, { name: 'released_at', type: 'timestamp', constraint: 'not null' }, { name: 'updated_at', type: 'timestamp', constraint: 'not null' }, + { name: 'author', type: 'text', constraint: 'not null' }, + { name: 'email', type: 'text', constraint: 'not null' }, + { name: 'title', type: 'text', constraint: 'not null' }, + { name: 'category', type: 'text', constraint: 'not null' }, { name: 'tags', type: 'text[]', constraint: 'not null' }, { name: 'image', type: 'text', constraint: '' }, { name: 'publish', type: 'text', constraint: 'not null' }, @@ -80,73 +75,49 @@ export default async function init(db: Postgres) { { name: 'ref_count', type: 'integer', constraint: 'not null' }, ], } - ]; - - await db.begin(); - try { - // Create tables - for (const schema of schemas) { - const res = await db.query(`select * from information_schema.tables where table_name = '${schema.name}'`) - if (res.rowCount == 0) { - console.log(`Creating table ${schema.name}`); - const columnStr = schema.columns.map(c => `${c.name} ${c.type} ${c.constraint}`).join(', '); - await db.query(`create table ${schema.name} (${columnStr})`); - } else { - console.log(`Table ${schema.name} already exists`); - } - } - } catch (err) { - console.error(err); - await db.rollback(); - } finally { - await db.commit(); - } - - const articleFiles: ArticleFileItem[] = []; - function scanDir(path: string) { - const files = fs.readdirSync(path); - for (const file of files) { - const dir = `${path}/${file}`; - const stat = fs.statSync(dir); - if (stat.isDirectory()) { - scanDir(dir); - } else { - articleFiles.push({ path: `${path}/${file}`, id: file.replace('.md', '') }); - } - } - } - scanDir('./articles/article'); + ]); + const articleFiles = await crawlArticles(db); await db.query('update tag set ref_count = 0'); for (const { path, id } of articleFiles) { + console.log(`Processing ${id}...`); await db.begin(); try { + const gitlog = await article_git.log({ + file: path.slice(11), + strictDate: true, + }); const res = await db.query('select * from article where id = $1', [id]); - const compiled = await compile(fs.readFileSync(path, 'utf-8')); - const title = compiled.data.fm.title; + const author = gitlog.all[0].author_name; + const email = gitlog.all[0].author_email; + const released_at = new Date(gitlog.all[0].date); + const updated_at = (gitlog.latest === null) ? released_at : new Date(gitlog.latest.date); + console.log(`Author: ${author} <${email}>\nReleased at: ${format(released_at)}\nUpdated at: ${format(updated_at)}`); + const category = path.split('/')[3]; + const compiled = await compile(fs.readFileSync(path, 'utf-8')); + const title = compiled.data.fm.title; const tags: string[] = compiled.data.fm.tags; - const released_at = new Date(compiled.data.fm.released_at); - const updated_at = (compiled.data.fm.updated_at !== null) ? new Date(compiled.data.fm.updated_at) : released_at; const image = compiled.data.fm.image; const publish = compiled.data.fm.publish; const content = compiled.code .replace(/>{@html ``}<\/pre>/g, '') + if (res.rowCount == 0) { console.log(`New article: ${id}`); await db.query( - 'insert into article (id, title, category, released_at, updated_at, tags, image, publish, content) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)', - [id, title, category, released_at, updated_at, tags, image, publish, content] + 'insert into article (id, released_at, updated_at, author, email, title, category, tags, image, publish, content) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)', + [id, released_at, updated_at, author, email, title, category, tags, image, publish, content] ); } else if (res.rows[0].updated_at < updated_at) { // } else if (true) { console.log(`Update article: ${id}`); await db.query( - 'update article set title = $2, category = $3, released_at = $4, updated_at = $5, tags = $6, image = $7, publish = $8, content = $9 where id = $1', - [id, title, category, released_at, updated_at, tags, image, publish, content] + 'update article set released_at = $2, updated_at = $3, author = $4, email = $5, title = $6, category = $7, tags = $8, image = $9, publish = $10, content = $11 where id = $1', + [id, released_at, updated_at, author, email, title, category, tags, image, publish, content] ); } else { console.log(`Article ${id} is already up-to-date`); @@ -162,6 +133,7 @@ export default async function init(db: Postgres) { console.log(err); await db.rollback(); } finally { + console.log(''); await db.commit(); } } @@ -180,4 +152,53 @@ export type TableSchema = { type: string, constraint: string, }[], +} + +const cloneRepo = async () => { + if (fs.existsSync('./articles/')) { + console.log('Pulling articles from git..'); + await article_git.pull(); + } else { + console.log('Cloning articles from git..'); + await article_git.clone('git@gitea.hareworks.net:Hare/blog-articles.git', 'articles'); + } +} + +const createTable = async (db: Postgres, schemas: TableSchema[]) => { + await db.begin(); + try { + for (const schema of schemas) { + const res = await db.query(`select * from information_schema.tables where table_name = '${schema.name}'`) + if (res.rowCount == 0) { + console.log(`Creating table ${schema.name}`); + const columnStr = schema.columns.map(c => `${c.name} ${c.type} ${c.constraint}`).join(', '); + await db.query(`create table ${schema.name} (${columnStr})`); + } else { + console.log(`Table ${schema.name} already exists`); + } + } + } catch (err) { + console.error(err); + await db.rollback(); + } finally { + await db.commit(); + } +} + +const crawlArticles = async (db: Postgres): Promise => { + const articleFiles: ArticleFileItem[] = []; + function scanDir(path: string) { + const files = fs.readdirSync(path); + for (const file of files) { + const dir = `${path}/${file}`; + const stat = fs.statSync(dir); + if (stat.isDirectory()) { + scanDir(dir); + } else { + articleFiles.push({ path: `${path}/${file}`, id: file.replace('.md', '') }); + } + } + } + scanDir('./articles/article'); + return articleFiles; } \ No newline at end of file -- 2.43.0 From 36237f0708c17bc308169583de5292097ecb3a33 Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 25 Sep 2024 23:29:21 +0900 Subject: [PATCH 4/4] feat: apply author and email fields to Articles --- articles | 2 +- src/lib/app.scss | 4 +- src/lib/article.ts | 2 + src/lib/blog.scss | 404 +++++++++++----------- src/lib/page/article_page.svelte | 111 ++++-- src/lib/server/aritcle-git.ts | 15 + src/lib/server/article.ts | 1 + src/lib/server/database/get_connection.ts | 6 +- src/lib/server/database/init.ts | 16 +- src/routes/+page.server.ts | 3 +- src/routes/+page.svelte | 12 +- 11 files changed, 328 insertions(+), 248 deletions(-) create mode 100644 src/lib/server/aritcle-git.ts diff --git a/articles b/articles index 3744bd7..d1d724d 160000 --- a/articles +++ b/articles @@ -1 +1 @@ -Subproject commit 3744bd78995594e5fdfcaeff32f426f1d41daffa +Subproject commit d1d724d1170e8cd2460cf1beb5098161ac33ef9a diff --git a/src/lib/app.scss b/src/lib/app.scss index 37f2f17..a6480d9 100644 --- a/src/lib/app.scss +++ b/src/lib/app.scss @@ -6,7 +6,7 @@ } :root { - font-size: 16px; + font-size: 18px; } body { @@ -15,5 +15,5 @@ body { color: #ffffff; font-family: 'ZenKakuGothicNew-Regular', '游ゴシック体', 'Yu Gothic', YuGothic, 'ヒラギノ角ゴシック Pro', 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, Osaka, 'MS PGothic', sans-serif; - font-size: 16px; + font-size: 1rem; } \ No newline at end of file diff --git a/src/lib/article.ts b/src/lib/article.ts index 352397d..bcb748f 100644 --- a/src/lib/article.ts +++ b/src/lib/article.ts @@ -7,6 +7,8 @@ export type Article = { category: string; released_at: Date; updated_at: Date; + author: string; + email: string; tags: string[]; image: string; publish: Publish; diff --git a/src/lib/blog.scss b/src/lib/blog.scss index 95a98c9..ea34c95 100644 --- a/src/lib/blog.scss +++ b/src/lib/blog.scss @@ -1,231 +1,243 @@ .document { - --color-text: #ffffff; - --color-concept: hsla(40, 100%, 90%, 0.5); - --color-concept-hsl: 39, 100%, 25%; - --color-outline: #ffffff40; - width: 100%; + --color-text: #ffffff; + --color-concept: hsla(40, 100%, 90%, 0.5); + --color-concept-hsl: 39, 100%, 25%; + --color-outline: #ffffff40; + width: 100%; - color: var(--color-text); + color: var(--color-text); - h1 { - position: relative; - width: auto; - font-size: 2rem; - margin-bottom: 0.5rem; - } + padding-left: 2rem; + box-sizing: border-box; - hr { - padding: 0; - margin: 0; - border: none; - border-top: 1px solid var(--color-outline); - } + h1 { + position: relative; + width: auto; + font-size: 1.8rem; + margin-bottom: 0.5rem; + margin-left: -1rem; + } - >*:not(h1, hr) { - margin-left: 2rem; - } + hr { + padding: 0; + margin: 0; + border: none; + border-top: 1px solid var(--color-outline); + margin-left: -1rem; + } - h2 { - font-size: 1.5rem; - position: relative; + h2 { + font-size: 1.5rem; + position: relative; - &::before { - content: '#'; - position: absolute; - top: 0; - left: -1.5rem; - font-size: 1.75rem; - color: rgb(131, 131, 131); - } - } + // &::before { + // content: '#'; + // position: absolute; + // top: 0; + // left: -1.5rem; + // font-size: 1.75rem; + // color: rgb(131, 131, 131); + // } + } - h3 { - font-size: 1.25rem; - } + h3 { + font-size: 1.25rem; + } - h4 { - font-size: 1.05rem; - } + h4 { + font-size: 1.05rem; + } - hr { - border: none; - border-top: 1px solid var(--color-outline); - } + hr { + border: none; + border-top: 1px solid var(--color-outline); + } - img { - max-height: 300px; - } + img { + max-height: 300px; + } - a { - color: var(--color-text); - position: relative; - display: inline-block; - transform: translate(0, 0); - text-shadow: 0 0 1rem hsl(var(--color-concept-hsl)); - transition: 0.2s ease-out; - font-weight: normal; + a { + color: var(--color-text); + position: relative; + display: inline-block; + transform: translate(0, 0); + text-shadow: 0 0 1rem hsl(var(--color-concept-hsl)); + transition: 0.2s ease-out; + font-weight: normal; - &:hover { - transform: translate(0, -0.1rem); - } - } + &:hover { + transform: translate(0, -0.1rem); + } + } - blockquote { - position: relative; - font-size: 1.1rem; - font-weight: bold; - padding: 0; - padding-left: 1rem; - color: rgb(162, 162, 162); + blockquote { + position: relative; + font-size: 1.1rem; + font-weight: bold; + padding: 0; + padding-left: 1rem; + color: rgb(162, 162, 162); - &::before { - content: '“'; - position: absolute; - top: 0; - left: -0.5rem; - font-size: 2rem; - color: rgb(131, 131, 131); - } - } + &::before { + content: '“'; + position: absolute; + top: 0; + left: -0.5rem; + font-size: 2rem; + color: rgb(131, 131, 131); + } + } - warn { - position: relative; - display: block; - padding: 1rem 0.7rem 0.7rem; - background-image: linear-gradient(to right, var(--color-outline), transparent 75%); - background-origin: border-box; - box-shadow: inset 0 0 0 100vh var(--background-color); - text-shadow: 0 0 1rem hsl(var(--color-concept-hsl), 1); - border: 1px solid transparent; + warn { + position: relative; + display: block; + padding: 1rem 0.7rem 0.7rem; + background-image: linear-gradient(to right, var(--color-outline), transparent 75%); + background-origin: border-box; + box-shadow: inset 0 0 0 100vh var(--background-color); + text-shadow: 0 0 1rem hsl(var(--color-concept-hsl), 1); + border: 1px solid transparent; + border-radius: 0.5rem; + + &::before { + content: 'note:warn'; + position: relative; + display: block; + top: 0; + font-size: 0.8rem; + margin-bottom: -0.3rem; + transform: translate(0, -0.5rem); + text-shadow: 0 0 1rem red; + } + } + + code:not(pre>code) { + display: inline-block; + font-family: monospace; + font-size: 0.9rem; + background: #66666666; + padding: 0 0.3rem; border-radius: 0.5rem; + } - &::before { - content: 'note:warn'; - position: relative; - display: block; - top: 0; - font-size: 0.8rem; - margin-bottom: -0.3rem; - transform: translate(0, -0.5rem); - text-shadow: 0 0 1rem red; - } - } + pre { + font-family: monospace; + position: relative; + display: block; + margin-top: 0.75rem; + margin-bottom: 0.75rem; + // border: 2px solid #ffffff40; + border-radius: 0.5rem; + overflow-x: auto; + font-size: 0.9rem; - pre { - font-family: monospace; - position: relative; - display: block; - margin-top: 0.75rem; - margin-bottom: 0.75rem; - border: 2px solid #ffffff40; - border-radius: 0.5rem; - overflow-x: scroll; + &::-webkit-scrollbar { + border-radius: 10px; + height: 10px; + width: 8px; + background: #3d3d3d; + } - &::-webkit-scrollbar { - border-radius: 10px; - height: 10px; - width: 8px; - } + &::-webkit-scrollbar-thumb { + background: #6f6f6f; + border-radius: 10px; + } - &::-webkit-scrollbar-thumb { - background: #999; - border-radius: 10px; - } + &::-webkit-scrollbar-track { + border-radius: 10px; + } - &::-webkit-scrollbar-track { - border-radius: 10px; - } + > code { + display: inline-block; + padding: 0.5rem; + border-radius: 0.6rem; + font-family: monospace; + box-sizing: border-box; + min-width: 100%; + } + } - >code { - display: inline-block; - padding: 0.5rem; - border-radius: 0.6rem; - font-family: monospace; - box-sizing: border-box; - min-width: 100%; - } - } + details { + position: relative; + display: block; + border-radius: 0.5rem; + transition: 0.2s ease-out; + transition-property: height; + padding-inline-start: 0.1rem; - details { - position: relative; - display: block; - border-radius: 0.5rem; - transition: 0.2s ease-out; - transition-property: height; - padding-inline-start: 0.1rem; + > summary { + position: relative; + border-radius: 0.5rem; + list-style: '+ ' outside; + margin-left: 1rem; + padding: 0.2rem; + background: linear-gradient(to right, rgba(255, 255, 255, 0.25), transparent 200px); + cursor: pointer; - >summary { - position: relative; - border-radius: 0.5rem; - list-style: '+ ' outside; - margin-left: 1rem; - padding: 0.2rem; - background: linear-gradient(to right, rgba(255, 255, 255, 0.25), transparent 200px); - cursor: pointer; + &:hover { + text-shadow: 0 0 0.25rem rgb(255, 255, 255); + } + } - &:hover { - text-shadow: 0 0 0.25rem rgb(255, 255, 255); - } - } + > p { + margin-block-start: 0.5rem; + margin-block-end: 0.5rem; + margin-left: 1rem; + display: none; + } - >p { - margin-block-start: 0.5rem; - margin-block-end: 0.5rem; - margin-left: 1rem; - display: none; - } + &[open] { + > summary { + list-style: '- ' outside; + } - &[open] { - >summary { - list-style: '- ' outside; - } + > p { + animation: detailsIn 0.5s ease; + display: block; + } + } + } - >p { - animation: detailsIn 0.5s ease; - display: block; - } - } - } + @keyframes detailsIn { + 0% { + opacity: 0; + transform: translateY(-10px); + } - @keyframes detailsIn { - 0% { - opacity: 0; - transform: translateY(-10px); - } + 100% { + opacity: 1; + transform: none; + } + } - 100% { - opacity: 1; - transform: none; - } - } + table { + border-spacing: 0; + border: none; + border: 1px solid var(--color-outline); + border-radius: 10px; + overflow: hidden; + box-shadow: 0 0 0.5rem hsl(var(--color-concept-hsl), 0.5); - table { - border-spacing: 0; - border: none; - border: 1px solid var(--color-outline); - border-radius: 10px; - overflow: hidden; - box-shadow: 0 0 0.5rem hsl(var(--color-concept-hsl), 0.5); + thead { + background-color: rgba(0, 0, 0, 0.1); + color: #fff; + } - thead { - background-color: rgba(0, 0, 0, 0.1); - color: #fff; - } + tr { + &:nth-child(2n) { + background-color: #ffffff0d; + } + } - tr { - &:nth-child(2n) { - background-color: #ffffff0d; - } - } + td, + th { + padding: 0.5em; + border-left: 1px solid var(--color-outline); - td, - th { - padding: 0.5em; - border-left: 1px solid var(--color-outline); - - &:first-child { - border-left: none; - } - } - } -} \ No newline at end of file + &:first-child { + border-left: none; + } + } + } +} diff --git a/src/lib/page/article_page.svelte b/src/lib/page/article_page.svelte index 204ace7..7cfaa53 100644 --- a/src/lib/page/article_page.svelte +++ b/src/lib/page/article_page.svelte @@ -8,7 +8,7 @@ import type { Article } from '$lib/article'; export let data: Article; - const isUpdated = data.updated_at.getTime() != data.released_at.getTime(); + const isUpdated = data.updated_at.getTime() != data.released_at.getTime(); @@ -21,7 +21,7 @@ {#if isUpdated} {/if} - + @@ -35,20 +35,29 @@

{data.title}

- - released - {#if isUpdated}
- updated - {/if} -
- - {data.category[0].toUpperCase() + data.category.slice(1)} - {#each data.tags as tag} - {tag} - {/each} - +
+ + released + {#if isUpdated}
+ updated + {/if} +
+ + {data.category[0].toUpperCase() + data.category.slice(1)} + {#each data.tags as tag} + {tag} + {/each} + +
+
+ Author +
@{data.author}
+
@@ -91,6 +100,11 @@ height: 100%; object-fit: cover; } + .card { + position: fixed; + top: 0; + right: 0; + } main { border: 1px solid var(--line-primary); border-top: none; @@ -100,7 +114,7 @@ min-height: 100%; padding: 20px; padding-top: 100px; - max-width: 1000px; + max-width: 800px; margin: 0 auto; display: flex; flex-direction: column; @@ -110,7 +124,7 @@ font-size: 2rem; text-align: center; margin: 0; - padding: 12px 100px 12px 0; + padding: 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.3); box-sizing: border-box; display: flex; @@ -120,29 +134,56 @@ .meta { display: flex; justify-content: space-between; + flex-direction: row; padding: 0 20px; - font-size: 0.8rem; - span { - opacity: 0.6; - } - a { - margin-left: 5px; - padding: 0 5px; - color: inherit; - text-decoration: none; - &:hover { - text-decoration: underline; + font-size: 1rem; + > .left { + display: flex; + flex-direction: column; + justify-content: center; + align-items: left; + span { + opacity: 0.6; } - &:first-child { - &::before { - content: '🗀 '; + a { + margin-left: 5px; + padding: 0 5px; + color: inherit; + text-decoration: none; + &:hover { + text-decoration: underline; } - &::after { - content: '|'; - margin-left: 15px; + &:first-child { + margin-left: 0; + padding-left: 0; + &::before { + content: '🗀 '; + } + &::after { + content: '|'; + margin-left: 15px; + } } } } + > .right { + display: flex; + flex-direction: row; + justify-content: right; + align-items: center; + gap: 10px; + height: 50px; + margin: 10px; + + img { + width: 50px; + height: 50px; + border-radius: 15%; + } + div { + font-size: 1rem; + } + } } } .panel { diff --git a/src/lib/server/aritcle-git.ts b/src/lib/server/aritcle-git.ts new file mode 100644 index 0000000..dc2e70a --- /dev/null +++ b/src/lib/server/aritcle-git.ts @@ -0,0 +1,15 @@ +import { simpleGit, } from 'simple-git'; +import type { SimpleGit, SimpleGitOptions } from 'simple-git'; + +console.log(process.cwd()+'/articles'); +export const gitOptions: Partial = { + baseDir: process.cwd()+'/articles', + binary: 'git', + maxConcurrentProcesses: 6, + config: [ + 'core.sshCommand=ssh -i ../key -F /dev/null' + ], +}; + +const git: SimpleGit = simpleGit(gitOptions); +export default git; \ No newline at end of file diff --git a/src/lib/server/article.ts b/src/lib/server/article.ts index 5c5ae8b..c9c283e 100644 --- a/src/lib/server/article.ts +++ b/src/lib/server/article.ts @@ -1,3 +1,4 @@ +//@ts-ignore import { compile, escapeSvelte } from "mdsvex"; import { createHighlighter } from "shiki"; import { kanagawa_dark as theme } from "$lib/kanagawa" diff --git a/src/lib/server/database/get_connection.ts b/src/lib/server/database/get_connection.ts index 8cadd60..818b226 100644 --- a/src/lib/server/database/get_connection.ts +++ b/src/lib/server/database/get_connection.ts @@ -10,10 +10,14 @@ import { } from '$env/static/private' const connectionString = `postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}`; -const pool = new Pool({ connectionString }); +let pool = new Pool({ connectionString }); export const getConnection = () => pool; +export const reConnect = async () => { + pool = new Pool({ connectionString }); +} + import init_db from '$lib/server/database/init'; import PG from '$lib/server/database'; await init_db(await PG(pool)); \ No newline at end of file diff --git a/src/lib/server/database/init.ts b/src/lib/server/database/init.ts index be20422..b96b31e 100644 --- a/src/lib/server/database/init.ts +++ b/src/lib/server/database/init.ts @@ -90,10 +90,14 @@ export default async function init(db: Postgres) { }); const res = await db.query('select * from article where id = $1', [id]); - const author = gitlog.all[0].author_name; - const email = gitlog.all[0].author_email; - const released_at = new Date(gitlog.all[0].date); - const updated_at = (gitlog.latest === null) ? released_at : new Date(gitlog.latest.date); + // console.log(gitlog); + const latest = gitlog.latest!; + const oldest = gitlog.all[gitlog.all.length - 1]; + + const author = oldest.author_name; + const email = oldest.author_email; + const released_at = new Date(oldest.date); + const updated_at = new Date(latest.date); console.log(`Author: ${author} <${email}>\nReleased at: ${format(released_at)}\nUpdated at: ${format(updated_at)}`); const category = path.split('/')[3]; @@ -112,8 +116,8 @@ export default async function init(db: Postgres) { 'insert into article (id, released_at, updated_at, author, email, title, category, tags, image, publish, content) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)', [id, released_at, updated_at, author, email, title, category, tags, image, publish, content] ); - } else if (res.rows[0].updated_at < updated_at) { - // } else if (true) { + // } else if (res.rows[0].updated_at < updated_at) { + } else if (true) { console.log(`Update article: ${id}`); await db.query( 'update article set released_at = $2, updated_at = $3, author = $4, email = $5, title = $6, category = $7, tags = $8, image = $9, publish = $10, content = $11 where id = $1', diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 35af34e..306e33e 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,6 +1,7 @@ import { error } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; import PG from '$lib/server/database'; +import { format } from '$lib/scripts/formatted_date'; let data: { recent: { @@ -28,7 +29,7 @@ export const load: PageServerLoad = async ({ params, locals }) => { data.recent = recent_articles.rows.map((row) => ({ title: row.title, image: row.image, - date: row.updated_at.toISOString().slice(0, 10), + date: format(row.updated_at), link: `/article/${row.category}/${row.id}`, tags: row.tags, })); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 208e4b3..12f44c7 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -130,7 +130,7 @@ height: 100dvh; backdrop-filter: blur(2px); overflow-y: auto; - overflow-x: hidden; + overflow-x: hidden; } .controls { position: fixed; @@ -305,15 +305,15 @@ .tags { ul { display: flex; - flex-wrap: wrap; - justify-content: left; - flex-direction: row; + flex-wrap: wrap; + justify-content: left; + flex-direction: row; gap: 3px; li { font-size: 1rem; padding: 3px 5px; - border: 1px solid var(--line-primary); - border-radius: 5px; + border: 1px solid var(--line-primary); + border-radius: 5px; } } } -- 2.43.0