Compare commits

...
36 Commits
Author SHA1 Message Date
Hare 3e32df5761 chore: optimize Dockerfile for pnpm usage and build process 2024-12-18 07:14:14 +09:00
Hare 992c99d2fb update: refine layout and spacing in blog styles and article page 2024-12-18 07:14:01 +09:00
Hare 5c05db4580 update: layout styles in overview and article pages 2024-12-18 06:34:55 +09:00
Hare bafa8e4d18 change: add rehypeAutolinkHeadings plugin to article compiler 2024-12-18 06:07:46 +09:00
Hare b529e96a6b update: add headings overview 2024-12-18 06:02:28 +09:00
Hare 7f395bcfaa Merge branch 'master' into develop 2024-10-23 21:39:13 +09:00
Hare 97600ed402 change: svelte default to temp logo 2024-10-23 21:38:33 +09:00
Hare ae48a85ece change: discord invite link 2024-10-23 20:17:39 +09:00
Hare 9f162e83c4 Merge pull request 'fix: Clone and analyze the repository correctly' (#15) from develop into master
Reviewed-on: #15
2024-09-27 00:49:42 +09:00
Hare e4548bf10b Merge branch 'master' into develop 2024-09-27 00:49:35 +09:00
Hare 5b441ff0cf fix: Clone and analyze the repository correctly 2024-09-27 00:48:22 +09:00
Hare 76bd2a5e1b Merge pull request 'develop' (#14) from develop into master
Reviewed-on: #14
2024-09-27 00:17:58 +09:00
Hare e3a83ee561 Merge branch 'master' into develop 2024-09-27 00:17:51 +09:00
Hare dd057dd335 Refactor: Update imports for server modules 2024-09-27 00:16:57 +09:00
Hare f11bdf15fc fix typo #2 2024-09-26 23:44:12 +09:00
Hare 5ec1839ec7 fix typo 2024-09-26 23:43:51 +09:00
Hare 2fdcc1633d Merge pull request 'fix: initializing git during build' (#13) from develop into master
Reviewed-on: #13
2024-09-26 23:37:15 +09:00
Hare 6173dffba3 fix: initializing git during build 2024-09-26 23:36:12 +09:00
Hare e389a3d2f9 Merge branch 'develop' 2024-09-26 23:12:09 +09:00
Hare 4e2422fa09 Merge pull request 'feat-google-analytics' (#11) from feat-google-analytics into develop
Reviewed-on: #11
2024-09-26 23:07:52 +09:00
Hare 8d09641e5d Remove articles subproject 2024-09-26 23:06:41 +09:00
Hare 2b6e3bfa9f feat: Add Google Analytics tracking code to layout component 2024-09-26 23:05:42 +09:00
Hare 3797e33450 Merge branch 'develop' of gitea.hareworks.net:Hare/blog.hareworks.net into develop 2024-09-26 22:59:34 +09:00
Hare db831b0683 Merge pull request 'feat-git' (#10) from feat-git into master
Reviewed-on: #10
2024-09-25 23:30:08 +09:00
Hare c08bb9dcef Merge branch 'master' into feat-git 2024-09-25 23:29:55 +09:00
Hare 36237f0708 feat: apply author and email fields to Articles 2024-09-25 23:29:21 +09:00
Hare 3145073381 feat: add a column to store datafrom git 2024-09-21 14:51:15 +09:00
Hare 7032c552a8 feat: Refactor formatted_date component and add format function 2024-09-18 17:32:02 +09:00
Hare 121cb60a5f feat: Add ress.css for global CSS reset 2024-09-18 17:31:39 +09:00
Hare 7cd8db36e2 feat: Add simple-git dependency 2024-09-18 17:31:06 +09:00
Hare e251697e92 Update blog logo and URL in README.md 2024-09-18 14:54:07 +09:00
Hare 58ce0f28c7 Merge pull request 'feat: codeblock highlighter and styling' (#9) from develop into master
Reviewed-on: #9
2024-09-07 02:49:35 +09:00
Hare 1637776b12 Merge branch 'master' into develop 2024-09-07 02:49:23 +09:00
Hare 19d63ed0d5 Merge pull request 'style tweaks' (#8) from develop into master
Reviewed-on: #8
2024-09-03 21:37:03 +09:00
Hare 41674ea6a1 Merge pull request 'feat: change article managment: git submodules to dynamic git clone&pull' (#7) from develop into master
Reviewed-on: #7
2024-09-03 07:01:14 +09:00
Hare 78d733ace6 Merge pull request 'chore: fix git install target from builder to runner' (#6) from develop into master
Reviewed-on: #6
2024-09-03 05:24:49 +09:00
32 changed files with 3935 additions and 4672 deletions
+6 -33
View File
@@ -1,38 +1,11 @@
# create-svelte # blog.hareworks.net
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). <img src="./static/img/logo.png" width="200">
## Creating a project ## About
If you're seeing this, you've probably already done this step. Congrats! This is the source code for my blog at [blog.hareworks.net](https://blog.hareworks.net).
```bash ## Made with SvelteKit
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app This blog is made with SvelteKit.
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
Submodule articles deleted from 3744bd7899
+11 -6
View File
@@ -1,19 +1,24 @@
# For Build # For Build
FROM node:22-slim as builder FROM node:22-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY package.json ./
COPY package-lock.json ./ COPY pnpm-lock.yaml ./
COPY tsconfig.json ./ COPY tsconfig.json ./
RUN npm ci
FROM base AS builder
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY . . COPY . .
RUN pnpm run build
RUN npm run build
# For Run FROM base
FROM node:22-slim RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
WORKDIR /app WORKDIR /app
-4120
View File
File diff suppressed because it is too large Load Diff
+25 -22
View File
@@ -1,6 +1,6 @@
{ {
"name": "blog.hareworks.net", "name": "blog.hareworks.net",
"version": "0.0.1", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
@@ -13,31 +13,34 @@
"format": "prettier --write ." "format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.28.1", "@playwright/test": "^1.49.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-node": "^5.2.2", "@sveltejs/adapter-node": "^5.2.10",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.12.1",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "@sveltejs/vite-plugin-svelte": "^5.0.2",
"@types/eslint": "^9.6.0", "@types/eslint": "^9.6.1",
"@types/pg": "^8.11.6", "@types/pg": "^8.11.10",
"eslint": "^9.0.0", "eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0", "eslint-plugin-svelte": "^2.46.1",
"globals": "^15.0.0", "globals": "^15.13.0",
"prettier": "^3.1.1", "prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.1.2", "prettier-plugin-svelte": "^3.3.2",
"shiki": "^1.16.2", "shiki": "^1.24.2",
"svelte": "^5.0.0-next.1", "svelte": "^5.14.1",
"svelte-check": "^3.6.0", "svelte-check": "4.1.1",
"typescript": "^5.0.0", "typescript": "^5.7.2",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.18.1",
"vite": "^5.0.3" "vite": "^6.0.3"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"dotenv": "^16.4.5", "dotenv": "^16.4.7",
"mdsvex": "^0.12.3", "mdsvex": "^0.12.3",
"pg": "^8.12.0", "pg": "^8.13.1",
"sass": "^1.77.8" "rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"sass": "^1.83.0",
"simple-git": "^3.27.0"
} }
} }
+3127
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,9 +1,11 @@
import type { Handle } from '@sveltejs/kit' import type { Handle } from '@sveltejs/kit'
import { getConnection } from '$lib/server/database/get_connection' import { getPool } from '$lib/server/database/pool';
import git from '$lib/server/git';
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const pg = getConnection(); const pg = getPool;
event.locals.db = pg; event.locals.db = pg;
event.locals.git = git;
const result = await resolve(event); const result = await resolve(event);
return result; return result;
+4 -3
View File
@@ -1,4 +1,5 @@
@import '../variables.scss'; @forward './ress.css';
@forward '../variables.scss';
@font-face { @font-face {
font-family: 'ZenKakuGothicNew-Regular'; font-family: 'ZenKakuGothicNew-Regular';
@@ -6,7 +7,7 @@
} }
:root { :root {
font-size: 16px; font-size: 18px;
} }
body { body {
@@ -15,5 +16,5 @@ body {
color: #ffffff; color: #ffffff;
font-family: 'ZenKakuGothicNew-Regular', '游ゴシック体', 'Yu Gothic', YuGothic, 'ヒラギノ角ゴシック Pro', font-family: 'ZenKakuGothicNew-Regular', '游ゴシック体', 'Yu Gothic', YuGothic, 'ヒラギノ角ゴシック Pro',
'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, Osaka, 'MS PGothic', sans-serif; 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, Osaka, 'MS PGothic', sans-serif;
font-size: 16px; font-size: 1rem;
} }
+2
View File
@@ -7,6 +7,8 @@ export type Article = {
category: string; category: string;
released_at: Date; released_at: Date;
updated_at: Date; updated_at: Date;
author: string;
email: string;
tags: string[]; tags: string[];
image: string; image: string;
publish: Publish; publish: Publish;
+241 -196
View File
@@ -1,231 +1,276 @@
.document { .document {
--color-text: #ffffff; --color-text: #ffffff;
--color-concept: hsla(40, 100%, 90%, 0.5); --color-concept: hsla(40, 100%, 90%, 0.5);
--color-concept-hsl: 39, 100%, 25%; --color-concept-hsl: 39, 100%, 25%;
--color-outline: #ffffff40; --color-outline: #ffffff40;
width: 100%; width: 100%;
color: var(--color-text); color: var(--color-text);
h1 { padding-left: 0rem;
position: relative; box-sizing: border-box;
width: auto;
font-size: 2rem;
margin-bottom: 0.5rem;
}
hr { h1 {
padding: 0; position: relative;
margin: 0; width: auto;
border: none; font-size: 1.8rem;
border-top: 1px solid var(--color-outline); padding: 1.5rem 0 0.25rem 0;
} margin: 0 0 0.75rem 0rem;
border-bottom: 1px solid var(--color-outline);
}
>*:not(h1, hr) { hr {
margin-left: 2rem; padding: 0;
} margin: 0;
border: none;
border-top: 1px solid var(--color-outline);
margin-left: -1rem;
}
h2 { h2 {
font-size: 1.5rem; padding: 1rem 0 0.25rem 0;
position: relative; font-size: 1.5rem;
position: relative;
&::before { // &::before {
content: '#'; // content: '#';
position: absolute; // position: absolute;
top: 0; // top: 0;
left: -1.5rem; // left: -1.5rem;
font-size: 1.75rem; // font-size: 1.75rem;
color: rgb(131, 131, 131); // color: rgb(131, 131, 131);
} // }
} }
h3 { h3 {
font-size: 1.25rem; font-size: 1.25rem;
} }
h4 { h4 {
font-size: 1.05rem; font-size: 1.05rem;
} }
hr { hr {
border: none; border: none;
border-top: 1px solid var(--color-outline); border-top: 1px solid var(--color-outline);
} }
img { img {
max-height: 300px; max-height: 300px;
} }
a { a {
color: var(--color-text); color: var(--color-text);
position: relative; position: relative;
display: inline-block; display: inline-block;
transform: translate(0, 0); transform: translate(0, 0);
text-shadow: 0 0 1rem hsl(var(--color-concept-hsl)); text-shadow: 0 0 1rem hsl(var(--color-concept-hsl));
transition: 0.2s ease-out; transition: 0.2s ease-out;
font-weight: normal; font-weight: normal;
&:hover { &:hover {
transform: translate(0, -0.1rem); transform: translate(0, -0.1rem);
} }
} }
blockquote { ul,
position: relative; ol {
font-size: 1.1rem; list-style: none;
font-weight: bold; padding: 0;
padding: 0; margin: 0;
padding-left: 1rem; }
color: rgb(162, 162, 162);
&::before { li {
content: ''; position: relative;
position: absolute; padding-left: 1rem;
top: 0; margin-bottom: 0.5rem;
left: -0.5rem; &::before {
font-size: 2rem; content: '';
color: rgb(131, 131, 131); position: absolute;
} top: 0;
} left: 0;
color: var(--color-concept);
}
}
ul > li {
&::before {
content: '';
}
}
ol > li {
counter-increment: item;
&::before {
content: counter(item) '.';
}
}
warn { blockquote {
position: relative; position: relative;
display: block; font-size: 1.1rem;
padding: 1rem 0.7rem 0.7rem; font-weight: bold;
background-image: linear-gradient(to right, var(--color-outline), transparent 75%); padding: 0;
background-origin: border-box; padding-left: 1rem;
box-shadow: inset 0 0 0 100vh var(--background-color); color: rgb(162, 162, 162);
text-shadow: 0 0 1rem hsl(var(--color-concept-hsl), 1);
border: 1px solid transparent;
border-radius: 0.5rem;
&::before { &::before {
content: 'note:warn'; content: '';
position: relative; position: absolute;
display: block; top: 0;
top: 0; left: -0.5rem;
font-size: 0.8rem; font-size: 2rem;
margin-bottom: -0.3rem; color: rgb(131, 131, 131);
transform: translate(0, -0.5rem); }
text-shadow: 0 0 1rem red; }
}
}
pre { warn {
font-family: monospace; position: relative;
position: relative; display: block;
display: block; padding: 1rem 0.7rem 0.7rem;
margin-top: 0.75rem; background-image: linear-gradient(to right, var(--color-outline), transparent 75%);
margin-bottom: 0.75rem; background-origin: border-box;
border: 2px solid #ffffff40; box-shadow: inset 0 0 0 100vh var(--background-color);
border-radius: 0.5rem; text-shadow: 0 0 1rem hsl(var(--color-concept-hsl), 1);
overflow-x: scroll; border: 1px solid transparent;
border-radius: 0.5rem;
&::-webkit-scrollbar { &::before {
border-radius: 10px; content: 'note:warn';
height: 10px; position: relative;
width: 8px; display: block;
} top: 0;
font-size: 0.8rem;
margin-bottom: -0.3rem;
transform: translate(0, -0.5rem);
text-shadow: 0 0 1rem red;
}
}
&::-webkit-scrollbar-thumb { code:not(pre > code) {
background: #999; display: inline-block;
border-radius: 10px; font-family: monospace;
} font-size: 0.9rem;
background: #66666666;
padding: 0 0.3rem;
border-radius: 0.5rem;
}
&::-webkit-scrollbar-track { pre {
border-radius: 10px; 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;
>code { &::-webkit-scrollbar {
display: inline-block; border-radius: 10px;
padding: 0.5rem; height: 10px;
border-radius: 0.6rem; width: 8px;
font-family: monospace; background: #3d3d3d;
box-sizing: border-box; }
min-width: 100%;
}
}
details { &::-webkit-scrollbar-thumb {
position: relative; background: #6f6f6f;
display: block; border-radius: 10px;
border-radius: 0.5rem; }
transition: 0.2s ease-out;
transition-property: height;
padding-inline-start: 0.1rem;
>summary { &::-webkit-scrollbar-track {
position: relative; border-radius: 10px;
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 { > code {
text-shadow: 0 0 0.25rem rgb(255, 255, 255); display: inline-block;
} padding: 0.5rem;
} border-radius: 0.6rem;
font-family: monospace;
box-sizing: border-box;
min-width: 100%;
}
}
>p { details {
margin-block-start: 0.5rem; position: relative;
margin-block-end: 0.5rem; display: block;
margin-left: 1rem; border-radius: 0.5rem;
display: none; transition: 0.2s ease-out;
} transition-property: height;
padding-inline-start: 0.1rem;
&[open] { > summary {
>summary { position: relative;
list-style: ' ' outside; 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;
>p { &:hover {
animation: detailsIn 0.5s ease; text-shadow: 0 0 0.25rem rgb(255, 255, 255);
display: block; }
} }
}
}
@keyframes detailsIn { > p {
0% { margin-block-start: 0.5rem;
opacity: 0; margin-block-end: 0.5rem;
transform: translateY(-10px); margin-left: 1rem;
} display: none;
}
100% { &[open] {
opacity: 1; > summary {
transform: none; list-style: ' ' outside;
} }
}
table { > p {
border-spacing: 0; animation: detailsIn 0.5s ease;
border: none; display: block;
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 { @keyframes detailsIn {
background-color: rgba(0, 0, 0, 0.1); 0% {
color: #fff; opacity: 0;
} transform: translateY(-10px);
}
tr { 100% {
&:nth-child(2n) { opacity: 1;
background-color: #ffffff0d; transform: none;
} }
} }
td, table {
th { border-spacing: 0;
padding: 0.5em; border: none;
border-left: 1px solid var(--color-outline); border: 1px solid var(--color-outline);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 0.5rem hsl(var(--color-concept-hsl), 0.5);
&:first-child { thead {
border-left: none; background-color: rgba(0, 0, 0, 0.1);
} color: #fff;
} }
}
} tr {
&:nth-child(2n) {
background-color: #ffffff0d;
}
}
td,
th {
padding: 0.5em;
border-left: 1px solid var(--color-outline);
&:first-child {
border-left: none;
}
}
}
}
+2 -15
View File
@@ -3,22 +3,9 @@
if (typeof date === 'string') date = new Date(date); if (typeof date === 'string') date = new Date(date);
const pad = function (str: string): string { import { format } from "$lib/scripts/formatted_date";
return ('0' + str).slice(-2);
};
const year = date.getFullYear().toString(); const formattedDate = format(date);
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}`;
</script> </script>
{formattedDate} {formattedDate}
+68
View File
@@ -0,0 +1,68 @@
<script lang="ts">
export let html: string;
const headings: {
level: number;
id: string;
text: string;
}[] = [];
html.match(/<h(\d) id="([^"]+)">([^<]+)<\/h\d>/g)?.forEach((match) => {
console.log(match);
const [, level, id, text] = match.match(/<h(\d) id="([^"]+)">([^<]+)<\/h\d>/)!;
headings.push({ level: +level, id, text });
});
let output = '';
let lastLevel = 1;
headings.forEach(({ level, id, text }) => {
console.log(level, id, text);
if (level > lastLevel) {
output += '<ul>';
} else if (level < lastLevel) {
output += '</li></ul>'.repeat(lastLevel - level);
} else {
if (output !== '') {
}
}
output += `<li><a href="#${id}">${text}</a>`;
lastLevel = level;
});
output += '</li>'.repeat(lastLevel);
</script>
<div class="overview">
<ul>
{@html output}
</ul>
</div>
<style lang="scss">
.overview {
margin: 0;
padding: 0;
:global(ul) {
list-style-type: none;
margin-left: 0.5em;
padding-left: 0.25em;
border-left: 1px solid var(--line-primary);
box-sizing: border-box;
}
ul {
border-left: none;
}
:global(li) {
padding: 0.5em 0.5em 0;
box-sizing: border-box;
}
:global(a) {
text-decoration: none;
color: var(--text-primary);
padding: 0.15em 0.4em;
border-radius: 0.25em;
text-wrap: nowrap;
box-sizing: border-box;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
}
}
</style>
+115 -49
View File
@@ -5,10 +5,11 @@
import Footer from '$lib/components/footer.svelte'; import Footer from '$lib/components/footer.svelte';
import * as publish from '$lib/publish'; import * as publish from '$lib/publish';
import FormattedDate from '$lib/components/formatted_date.svelte'; import FormattedDate from '$lib/components/formatted_date.svelte';
import Overview from '$lib/components/overview.svelte';
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
export let data: 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();
</script> </script>
<svelte:head> <svelte:head>
@@ -21,7 +22,7 @@
{#if isUpdated} {#if isUpdated}
<meta property="article:modified_time" content={data.updated_at.toISOString()} /> <meta property="article:modified_time" content={data.updated_at.toISOString()} />
{/if} {/if}
<meta property="article:author" content="HareWorks" /> <meta property="article:author" content={data.author} />
<meta property="article:section" content="Blog" /> <meta property="article:section" content="Blog" />
<meta property="article:tag" content={data.tags.join(',')} /> <meta property="article:tag" content={data.tags.join(',')} />
<meta name="robots" content={data.publish as publish.Publish} /> <meta name="robots" content={data.publish as publish.Publish} />
@@ -32,30 +33,46 @@
</div> --> </div> -->
<div class="container"> <div class="container">
<main> <main>
<div class="title"> <div class="content">
<h1>{data.title}</h1> <div class="title">
<div class="meta"> <h1>{data.title}</h1>
<span> <div class="meta">
released <FormattedDate date={data.released_at} /> <div class="left">
{#if isUpdated}<br /> <span>
updated <FormattedDate date={data.updated_at} /> released <FormattedDate date={data.released_at} />
{/if} {#if isUpdated}<br />
</span> updated <FormattedDate date={data.updated_at} />
<span> {/if}
<a href="/category/{data.category}" </span>
>{data.category[0].toUpperCase() + data.category.slice(1)}</a <span>
> <a href="/category/{data.category}"
{#each data.tags as tag} >{data.category[0].toUpperCase() + data.category.slice(1)}</a
<a href={`/search?tag=${tag}`}>{tag}</a> >
{/each} {#each data.tags as tag}
</span> <a href={`/search?tag=${tag}`}>{tag}</a>
{/each}
</span>
</div>
<div class="right">
<img
src="https://gitea.hareworks.net/avatars/e595ec10f8052671deab92caca78220fc838f63259bcd542a0d93f53e2371d52?size=512"
alt="Author"
/>
<div>@{data.author}</div>
</div>
</div>
</div>
<div class="panel">
<div class="document">
<slot />
</div>
<Footer />
</div> </div>
</div> </div>
<div class="panel"> <div class="card-container">
<div class="document"> <div class="card">
<slot /> <Overview html={data.content} />
</div> </div>
<Footer />
</div> </div>
</main> </main>
</div> </div>
@@ -91,26 +108,48 @@
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }
main { main {
border: 1px solid var(--line-primary);
border-top: none;
border-bottom: none;
box-sizing: border-box; box-sizing: border-box;
color: white; color: white;
min-height: 100%; display: flex;
flex-direction: row;
justify-content: center;
align-items: start;
.content {
border: 1px solid var(--line-primary);
border-top: none;
border-bottom: none;
min-height: 100%;
padding: 100px 100px 0 100px;
max-width: 900px;
margin: 0;
}
}
.card-container {
position: sticky;
top: 0;
background-color: var(--background-primary);
box-sizing: border-box;
padding: 20px; padding: 20px;
padding-top: 100px;
max-width: 1000px;
margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between;
overflow: visible;
.card {
width: 100px;
padding: 0.5em 0;
overflow: visible;
}
} }
.title { .title {
h1 { h1 {
font-size: 2rem; font-size: 2rem;
text-align: center; text-align: center;
margin: 0; margin: 0 -30px 0 -30px;
padding: 12px 100px 12px 0; padding: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.3); border-bottom: 1px solid rgba(255, 255, 255, 0.3);
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
@@ -120,29 +159,56 @@
.meta { .meta {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-direction: row;
padding: 0 20px; padding: 0 20px;
font-size: 0.8rem; font-size: 1rem;
span { > .left {
opacity: 0.6; display: flex;
} flex-direction: column;
a { justify-content: center;
margin-left: 5px; align-items: left;
padding: 0 5px; span {
color: inherit; opacity: 0.6;
text-decoration: none;
&:hover {
text-decoration: underline;
} }
&:first-child { a {
&::before { margin-left: 5px;
content: '🗀 '; padding: 0 5px;
color: inherit;
text-decoration: none;
&:hover {
text-decoration: underline;
} }
&::after { &:first-child {
content: '|'; margin-left: 0;
margin-left: 15px; 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 { .panel {
+1
View File
@@ -0,0 +1 @@
html{-webkit-text-size-adjust:100%;box-sizing:border-box;-moz-tab-size:4;tab-size:4;word-break:normal}*,:after,:before{background-repeat:no-repeat;box-sizing:inherit}:after,:before{text-decoration:inherit;vertical-align:inherit}*{margin:0;padding:0}hr{color:inherit;height:0;overflow:visible}details,main{display:block}summary{display:list-item}small{font-size:80%}[hidden]{display:none}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}a{background-color:transparent}a:active,a:hover{outline-width:0}code,kbd,pre,samp{font-family:monospace,monospace}pre{font-size:1em}b,strong{font-weight:bolder}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}iframe{border-style:none}input{border-radius:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}textarea{overflow:auto;resize:vertical}button,input,optgroup,select,textarea{font:inherit}optgroup{font-weight:700}button{overflow:visible}button,select{text-transform:none}[role=button],[type=button],[type=reset],[type=submit],button{cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button:-moz-focusring{outline:1px dotted ButtonText}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}button,input,select,textarea{background-color:transparent;border-style:none}a:focus,button:focus,input:focus,select:focus,textarea:focus{outline-width:0}select{-moz-appearance:none;-webkit-appearance:none}select::-ms-expand{display:none}select::-ms-value{color:currentColor}legend{border:0;color:inherit;display:table;max-width:100%;white-space:normal}::-webkit-file-upload-button{-webkit-appearance:button;color:inherit;font:inherit}[disabled]{cursor:default}img{border-style:none}progress{vertical-align:baseline}[aria-busy=true]{cursor:progress}[aria-controls]{cursor:pointer}[aria-disabled=true]{cursor:default}
+20
View File
@@ -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}`;
};
@@ -1,7 +1,9 @@
//@ts-ignore
import { compile, escapeSvelte } from "mdsvex"; import { compile, escapeSvelte } from "mdsvex";
import { createHighlighter } from "shiki"; import { createHighlighter } from "shiki";
import { kanagawa_dark as theme } from "$lib/kanagawa" import { kanagawa_dark as theme } from "$lib/kanagawa"
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeSlug from "rehype-slug";
const highlighter = await createHighlighter({ const highlighter = await createHighlighter({
themes: [theme], themes: [theme],
@@ -18,5 +20,6 @@ export default async function compileArticle(code: string) {
return html; return html;
}, },
}, },
rehypePlugins: [rehypeAutolinkHeadings, rehypeSlug],
}); });
} }
+144
View File
@@ -0,0 +1,144 @@
import type { Postgres } from '$lib/server/database/postgres';
import { type SimpleGit, CheckRepoActions } from 'simple-git';
import fs from 'fs';
import compileArticle from '$lib/server/article_compiler';
import type { TableSchema } from '$lib/server/database/table';
import server_table from '$lib/server/database/table';
import { format } from '$lib/scripts/formatted_date';
const load = async (db: Postgres, git: SimpleGit) => {
const articleFiles = await crawlArticles(db);
await db.query('update tag set ref_count = 0');
await db.begin();
let i = 0;
for (const { path, id } of articleFiles) {
await db.savepoint(`load${i}`);
console.log(`Processing ${id}...`);
try {
const gitlog = await git.log({
file: path.slice(11),
strictDate: true,
});
const res = await db.query('select * from article where id = $1', [id]);
// 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];
const compiled = await compileArticle(fs.readFileSync(path, 'utf-8'));
const title = compiled.data.fm.title;
const tags: string[] = compiled.data.fm.tags;
const image = compiled.data.fm.image;
const publish = compiled.data.fm.publish;
const content = compiled.code
.replace(/>{@html `<code class="language-/g, '><code class="language-')
.replace(/<\/code>`}<\/pre>/g, '</code></pre>')
if (res.rowCount == 0) {
console.log(`New article: ${id}`);
await db.query(
'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 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`);
}
for (const tag of tags) {
if ((await db.query('select * from tag where name = $1', [tag])).rowCount == 0) {
db.query('insert into tag (name, ref_count) values ($1, 1)', [tag]);
} else {
db.query('update tag set ref_count = ref_count + 1 where name = $1', [tag]);
}
}
} catch (err) {
console.log(err);
await db.rollbackTo(`load${i}`);
} finally {
console.log('');
await db.releaseSavepoint(`load${i}`);
i++;
}
}
await db.commit();
}
type ArticleFileItem = {
path: string,
id: string,
}
const cloneRepo = async (git: SimpleGit) => {
const isRepoRoot = await git.checkIsRepo(CheckRepoActions.IS_REPO_ROOT);
if (isRepoRoot) {
console.log('Pulling articles from git..');
await git.pull();
} else {
console.log('Cloning articles from git..');
await git.clone('git@gitea.hareworks.net:Hare/blog-articles.git', './');
}
}
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<ArticleFileItem[]> => {
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;
}
export async function init(db: Postgres, git: SimpleGit) {
await createTable(db, server_table);
await cloneRepo(git);
await load(db, git);
}
export async function reload(db: Postgres, git: SimpleGit) {
await cloneRepo(git);
await load(db, git);
}
-19
View File
@@ -1,19 +0,0 @@
import pg from 'pg';
const { Pool } = pg;
import {
PG_USER,
PG_PASS,
PG_HOST,
PG_PORT,
PG_DB,
} from '$env/static/private'
const connectionString = `postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}`;
const pool = new Pool({ connectionString });
export const getConnection = () => pool;
import init_db from '$lib/server/database/init';
import PG from '$lib/server/database';
await init_db(await PG(pool));
-183
View File
@@ -1,183 +0,0 @@
// initialize
import type { Postgres } from '$lib/server/database';
import fs from 'fs';
import { execSync } from 'child_process';
import compile from '$lib/server/article';
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 = [
{
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: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'image', type: 'text', constraint: '' },
{ name: 'publish', type: 'text', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'article_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'article', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread',
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: 'created_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_post',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'thread_id', type: 'integer', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'thread', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'tag',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'name', type: 'text', constraint: 'not null' },
{ 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');
await db.query('update tag set ref_count = 0');
for (const { path, id } of articleFiles) {
await db.begin();
try {
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 category = path.split('/')[3];
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 `<code class="language-/g, '><code class="language-')
.replace(/<\/code>`}<\/pre>/g, '</code></pre>')
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]
);
} 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]
);
} else {
console.log(`Article ${id} is already up-to-date`);
}
for (const tag of tags) {
if ((await db.query('select * from tag where name = $1', [tag])).rowCount == 0) {
db.query('insert into tag (name, ref_count) values ($1, 1)', [tag]);
} else {
db.query('update tag set ref_count = ref_count + 1 where name = $1', [tag]);
}
}
} catch (err) {
console.log(err);
await db.rollback();
} finally {
await db.commit();
}
}
await db.release();
}
type ArticleFileItem = {
path: string,
id: string,
}
export type TableSchema = {
name: string,
columns: {
name: string,
type: string,
constraint: string,
}[],
}
+24
View File
@@ -0,0 +1,24 @@
import pg from 'pg';
const { Pool } = pg;
import {
PG_USER,
PG_PASS,
PG_HOST,
PG_PORT,
PG_DB,
} from '$env/static/private'
const connectionString = `postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}`;
let pool = new Pool({ connectionString });
export const getPool = pool;
export const reConnect = async () => {
pool = new Pool({ connectionString });
}
import { init as init_data} from '$lib/server/data_loader';
import PG from '$lib/server/database/postgres';
import git from '$lib/server/git';
await init_data(await PG(pool), git);
@@ -24,9 +24,21 @@ export class Postgres {
await this.client!.query('commit'); await this.client!.query('commit');
} }
async savepoint(name: string) {
await this.client!.query(`savepoint ${name}`);
}
async rollback() { async rollback() {
await this.client!.query('rollback'); await this.client!.query('rollback');
} }
async rollbackTo(name: string) {
await this.client!.query(`rollback to ${name}`);
}
async releaseSavepoint(name: string) {
await this.client!.query(`release savepoint ${name}`);
}
} }
export default async ( export default async (
+77
View File
@@ -0,0 +1,77 @@
export type TableSchema = {
name: string,
columns: {
name: string,
type: string,
constraint: string,
}[],
}
export default [
{
name: 'article',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'id', 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' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'article_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'article', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread',
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: 'created_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_post',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'thread_id', type: 'integer', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'thread', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'tag',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'name', type: 'text', constraint: 'not null' },
{ name: 'ref_count', type: 'integer', constraint: 'not null' },
],
}
] as TableSchema[];
+17
View File
@@ -0,0 +1,17 @@
import fs from 'fs';
import { simpleGit, } from 'simple-git';
import type { SimpleGit, SimpleGitOptions } from 'simple-git';
console.log(process.cwd()+'/articles');
fs.mkdirSync(process.cwd()+'/articles', { recursive: true });
export const gitOptions: Partial<SimpleGitOptions> = {
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;
+15 -2
View File
@@ -1,5 +1,18 @@
<script> <script>
import '$lib/app.scss'; import '$lib/app.scss';
</script> </script>
<slot /> <svelte:head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-6D99J1R7DX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-6D99J1R7DX');
</script>
</svelte:head>
<slot />
+3 -2
View File
@@ -1,6 +1,7 @@
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { format } from '$lib/scripts/formatted_date';
let data: { let data: {
recent: { recent: {
@@ -28,7 +29,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
data.recent = recent_articles.rows.map((row) => ({ data.recent = recent_articles.rows.map((row) => ({
title: row.title, title: row.title,
image: row.image, image: row.image,
date: row.updated_at.toISOString().slice(0, 10), date: format(row.updated_at),
link: `/article/${row.category}/${row.id}`, link: `/article/${row.category}/${row.id}`,
tags: row.tags, tags: row.tags,
})); }));
+6 -6
View File
@@ -130,7 +130,7 @@
height: 100dvh; height: 100dvh;
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.controls { .controls {
position: fixed; position: fixed;
@@ -305,15 +305,15 @@
.tags { .tags {
ul { ul {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: left; justify-content: left;
flex-direction: row; flex-direction: row;
gap: 3px; gap: 3px;
li { li {
font-size: 1rem; font-size: 1rem;
padding: 3px 5px; padding: 3px 5px;
border: 1px solid var(--line-primary); border: 1px solid var(--line-primary);
border-radius: 5px; border-radius: 5px;
} }
} }
} }
+4 -4
View File
@@ -6,9 +6,9 @@ import {
TOKEN TOKEN
} from '$env/static/private' } from '$env/static/private'
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
// import { building } from '$app/environment'; import { building } from '$app/environment';
import init_db from '$lib/server/database/init'; import { reload as reload_data } from '$lib/server/data_loader';
export const POST: RequestHandler = async ({ url, locals }) => { export const POST: RequestHandler = async ({ url, locals }) => {
const token = url.searchParams.get('token'); const token = url.searchParams.get('token');
@@ -17,6 +17,6 @@ export const POST: RequestHandler = async ({ url, locals }) => {
return error(401, 'Unauthorized'); return error(401, 'Unauthorized');
} }
await init_db(await PG(locals.db)); if (!building) await reload_data(await PG(locals.db), locals.git);
return new Response(String(token)); return new Response(String(token));
}; };
@@ -1,6 +1,6 @@
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, locals }) => { export const load: PageServerLoad = async ({ params, locals }) => {
@@ -1,6 +1,6 @@
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, locals }) => { export const load: PageServerLoad = async ({ params, locals }) => {
+1 -1
View File
@@ -1,5 +1,5 @@
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
export function load() { export function load() {
throw redirect(308, 'https://discord.gg/9RfkJeQQfB'); throw redirect(308, 'https://discord.gg/hbHzw7JkFq');
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 10 KiB

-5
View File
@@ -3,14 +3,9 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter() adapter: adapter()
} }
}; };