├── .node-version ├── .npmrc ├── static ├── robots.txt ├── images │ └── profile.jpg ├── favicon.svg └── assets │ └── particles.json ├── src ├── lib │ ├── index.ts │ ├── stores.ts │ ├── types.ts │ ├── enums.ts │ ├── helpers.ts │ ├── translations.ts │ └── components │ │ ├── icons │ │ ├── AcrossMark.svelte │ │ ├── ArrowUp.svelte │ │ ├── Terraform.svelte │ │ ├── MusicNoteBeamed.svelte │ │ ├── Moon.svelte │ │ ├── Sun.svelte │ │ ├── Terminal.svelte │ │ ├── Easel.svelte │ │ ├── Language.svelte │ │ ├── Lightbulb.svelte │ │ ├── Cloud.svelte │ │ ├── FolderOpen.svelte │ │ ├── FileEarmarkRichText.svelte │ │ ├── Ansible.svelte │ │ ├── Floppy.svelte │ │ ├── ChatHeart.svelte │ │ ├── ClipboardCheck.svelte │ │ ├── GlobeAmericas.svelte │ │ ├── PersonWalking.svelte │ │ ├── JournalText.svelte │ │ ├── BatteryCharging.svelte │ │ ├── Bug.svelte │ │ ├── TailwindCss.svelte │ │ ├── Cpu.svelte │ │ ├── Nginx.svelte │ │ ├── K3s.svelte │ │ ├── GoogleCloudPlatform.svelte │ │ ├── HandThumbsUp.svelte │ │ ├── RocketTakeoff.svelte │ │ ├── Svelte.svelte │ │ ├── Controller.svelte │ │ ├── Php.svelte │ │ ├── Python.svelte │ │ ├── TypeScript.svelte │ │ ├── Laravel.svelte │ │ ├── CupHot.svelte │ │ ├── Azure.svelte │ │ ├── GitHubAction.svelte │ │ ├── Redis.svelte │ │ ├── Livewire.svelte │ │ ├── MySql.svelte │ │ ├── Aws.svelte │ │ └── Rust.svelte │ │ ├── layouts │ │ ├── Footer.svelte │ │ └── Header.svelte │ │ ├── Step.svelte │ │ ├── CircularProgressBar.svelte │ │ ├── Toggle.svelte │ │ ├── sections │ │ ├── About.svelte │ │ ├── Posts.svelte │ │ ├── Introduction.svelte │ │ ├── Project.svelte │ │ ├── Skill.svelte │ │ └── Experience.svelte │ │ ├── Modal.svelte │ │ └── Background.svelte ├── lang │ ├── zh-CN │ │ ├── footer.ts │ │ ├── header.ts │ │ ├── post.ts │ │ ├── introduction.ts │ │ ├── index.ts │ │ ├── about.ts │ │ ├── experience.ts │ │ ├── project.ts │ │ └── skill.ts │ ├── zh-TW │ │ ├── footer.ts │ │ ├── header.ts │ │ ├── post.ts │ │ ├── introduction.ts │ │ ├── index.ts │ │ ├── about.ts │ │ ├── experience.ts │ │ ├── project.ts │ │ └── skill.ts │ ├── en │ │ ├── footer.ts │ │ ├── post.ts │ │ ├── header.ts │ │ ├── index.ts │ │ ├── introduction.ts │ │ ├── about.ts │ │ ├── project.ts │ │ ├── experience.ts │ │ └── skill.ts │ ├── ja │ │ ├── footer.ts │ │ ├── header.ts │ │ ├── post.ts │ │ ├── introduction.ts │ │ ├── index.ts │ │ ├── about.ts │ │ ├── experience.ts │ │ ├── project.ts │ │ └── skill.ts │ ├── type │ │ ├── footer.type.ts │ │ ├── header.type.ts │ │ ├── post.type.ts │ │ ├── about.type.ts │ │ ├── introduction.type.ts │ │ ├── skill.type.ts │ │ ├── experience.type.ts │ │ ├── index.type.ts │ │ └── project.type.ts │ └── README.md ├── demo.spec.ts ├── routes │ ├── +page.server.ts │ ├── +layout.svelte │ └── [locale] │ │ ├── +page.server.ts │ │ └── +page.svelte ├── app.d.ts ├── app.html └── app.css ├── .prettierignore ├── .gitignore ├── playwright.config.ts ├── .prettierrc ├── README.md ├── svelte.config.js ├── tsconfig.json ├── vite.config.ts ├── eslint.config.js ├── e2e └── page.test.ts └── package.json /.node-version: -------------------------------------------------------------------------------- 1 | 25.2.0 -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /static/images/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/portfolio/main/static/images/profile.jpg -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore files for PNPM, NPM and YARN 2 | pnpm-lock.yaml 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /src/lang/zh-CN/footer.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | content_prefix: '本页面由', 3 | and: '与', 4 | content_suffix: '所制作' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/zh-TW/footer.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | content_prefix: '本頁面由', 3 | and: '與', 4 | content_suffix: '所製作' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/en/footer.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | content_prefix: 'Made with', 3 | and: 'and', 4 | content_suffix: '' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/ja/footer.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | content_prefix: 'このページは', 3 | and: 'と', 4 | content_suffix: 'で作成されました' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/ja/header.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | project: 'プロジェクト', 3 | experiences: '経歴', 4 | about: '私について', 5 | check_my_blog: 'ブログを読む' 6 | }; 7 | -------------------------------------------------------------------------------- /src/lang/zh-CN/header.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | project: '项目', 3 | experiences: '经历', 4 | about: '关于我', 5 | check_my_blog: '看看我的博客' 6 | }; 7 | -------------------------------------------------------------------------------- /src/lang/zh-TW/header.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | project: '專案', 3 | experiences: '經歷', 4 | about: '關於我', 5 | check_my_blog: '看看我的部落格' 6 | }; 7 | -------------------------------------------------------------------------------- /src/lang/ja/post.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '私の', 3 | section_title_highlight: '最新の記事', 4 | section_title_suffix: '' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/zh-CN/post.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '我的', 3 | section_title_highlight: '最新文章', 4 | section_title_suffix: '' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/zh-TW/post.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '我的', 3 | section_title_highlight: '最新文章', 4 | section_title_suffix: '' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lang/en/post.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: 'My', 3 | section_title_highlight: 'latest posts', 4 | section_title_suffix: '' 5 | }; 6 | -------------------------------------------------------------------------------- /src/lib/stores.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from '$lib/enums'; 2 | import { writable } from 'svelte/store'; 3 | 4 | export const theme = writable(Theme.Light); 5 | -------------------------------------------------------------------------------- /src/lang/en/header.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | project: 'Project', 3 | experiences: 'Experiences', 4 | about: 'About', 5 | check_my_blog: 'Check My Blog' 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface Post { 2 | id: number; 3 | title: string; 4 | excerpt: string; 5 | created_at: string; 6 | updated_at: string; 7 | url: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/lang/type/footer.type.ts: -------------------------------------------------------------------------------- 1 | type FooterTranslation = { 2 | content_prefix: string; 3 | and: string; 4 | content_suffix: string; 5 | }; 6 | 7 | export type { FooterTranslation }; 8 | -------------------------------------------------------------------------------- /src/demo.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | 3 | describe('sum test', () => { 4 | it('adds 1 + 2 to equal 3', () => { 5 | expect(1 + 2).toBe(3); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | .idea 12 | -------------------------------------------------------------------------------- /src/lang/type/header.type.ts: -------------------------------------------------------------------------------- 1 | type HeaderTranslation = { 2 | project: string; 3 | experiences: string; 4 | about: string; 5 | check_my_blog: string; 6 | }; 7 | 8 | export type { HeaderTranslation }; 9 | -------------------------------------------------------------------------------- /src/lib/enums.ts: -------------------------------------------------------------------------------- 1 | export enum Theme { 2 | Light = 'light', 3 | Dark = 'dark' 4 | } 5 | 6 | export enum Locale { 7 | En = 'en', 8 | Cn = 'zh-cn', 9 | Tw = 'zh-tw', 10 | Ja = 'ja' 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/type/post.type.ts: -------------------------------------------------------------------------------- 1 | type PostTranslation = { 2 | section_title_prefix: string; 3 | section_title_highlight: string; 4 | section_title_suffix: string; 5 | }; 6 | 7 | export type { PostTranslation }; 8 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | webServer: { 5 | command: 'npm run build && npm run preview', 6 | port: 4173 7 | }, 8 | testDir: 'e2e' 9 | }); 10 | -------------------------------------------------------------------------------- /src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { PageServerLoad } from './$types'; 2 | import { Locale } from '$lib/enums'; 3 | import { redirect } from '@sveltejs/kit'; 4 | 5 | export const load: PageServerLoad = async () => { 6 | redirect(301, `/${Locale.Tw}`); 7 | }; 8 | -------------------------------------------------------------------------------- /src/lang/type/about.type.ts: -------------------------------------------------------------------------------- 1 | type AboutTranslation = { 2 | section_title_prefix: string; 3 | section_title_highlight: string; 4 | section_title_suffix: string; 5 | I_am_also: string; 6 | strengths: { name: string; description: string }[]; 7 | }; 8 | 9 | export type { AboutTranslation }; 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": [ 7 | "prettier-plugin-svelte", 8 | "prettier-plugin-tailwindcss" 9 | ], 10 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Portfolio 2 | 3 | A simple static site to introduce myself and show the collection of projects I have worked on. 4 | 5 | This project is made by [SvelteKit](https://kit.svelte.dev/). 6 | 7 | ## Requirements 8 | 9 | - [node](https://nodejs.org/en) ^19 10 | - [npm](https://www.npmjs.com/) 11 | 12 | -------------------------------------------------------------------------------- /src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | export function formatDate(iso: string) { 2 | try { 3 | const d = new Date(iso); 4 | 5 | return new Intl.DateTimeFormat(navigator.language || 'en-US', { 6 | year: 'numeric', 7 | month: 'short', 8 | day: '2-digit' 9 | }).format(d); 10 | } catch { 11 | return iso; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /src/lib/translations.ts: -------------------------------------------------------------------------------- 1 | import type { Translation } from '$lang/type/index.type'; 2 | import english from '$lang/en'; 3 | import simplifiedChinese from '$lang/zh-CN'; 4 | import traditionalChinese from '$lang/zh-TW'; 5 | import japanese from '$lang/ja'; 6 | 7 | export const translations: { [Name: string]: Translation } = { 8 | en: english, 9 | 'zh-cn': simplifiedChinese, 10 | 'zh-tw': traditionalChinese, 11 | ja: japanese 12 | }; 13 | -------------------------------------------------------------------------------- /src/lang/zh-CN/introduction.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | i_am: '嗨!我是', 3 | a: '是一位', 4 | occupation: '工程师', 5 | introduction_part_1: '后端打工仔。擅长各类', 6 | introduction_highlight_part_1: '运维技能', 7 | introduction_part_2: '与', 8 | introduction_highlight_part_2: '云端服务', 9 | introduction_part_3: ',下班后喜欢不务正业地研究', 10 | introduction_highlight_part_3: '前后端技术', 11 | introduction_part_4: '。个性就像动态语言般随性,但渴望做事能像啰嗦的静态语言那样严谨。', 12 | introduction_highlight_part_4: '' 13 | }; 14 | -------------------------------------------------------------------------------- /src/lang/zh-TW/introduction.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | i_am: '嗨!我是', 3 | a: '是一位', 4 | occupation: '工程師', 5 | introduction_part_1: '後端打工仔。擅長各類', 6 | introduction_highlight_part_1: '維運技能', 7 | introduction_part_2: '與', 8 | introduction_highlight_part_2: '雲端服務', 9 | introduction_part_3: ',下班後喜歡不務正業的研究', 10 | introduction_highlight_part_3: '前後端技術', 11 | introduction_part_4: '。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。', 12 | introduction_highlight_part_4: '' 13 | }; 14 | -------------------------------------------------------------------------------- /src/lib/components/icons/AcrossMark.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/lib/components/icons/ArrowUp.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 13 |
%sveltekit.body%
14 | 15 | 16 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://svelte.dev/docs/kit/integrations 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | kit: { 10 | alias: { 11 | $lang: './src/lang' 12 | }, 13 | adapter: adapter() 14 | } 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /static/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lang/ja/introduction.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | i_am: 'こんにちは!私は', 3 | a: '', 4 | occupation: 'エンジニアです', 5 | introduction_part_1: '', 6 | introduction_highlight_part_1: '運用', 7 | introduction_part_2: 'と', 8 | introduction_highlight_part_2: 'クラウドサービス', 9 | introduction_part_3: 'に長けたバックエンドエンジニア。余暇には、', 10 | introduction_highlight_part_3: 'フロントエンドとバックエンドの両方の技術', 11 | introduction_part_4: 'を探求するのが好きです。性格は動的言語のよう自由だが、冗長な静的言語のように厳密さを追求したいと思っています。', 12 | introduction_highlight_part_4: '' 13 | }; 14 | -------------------------------------------------------------------------------- /src/lang/type/introduction.type.ts: -------------------------------------------------------------------------------- 1 | type IntroductionTranslation = { 2 | i_am: string; 3 | a: string; 4 | occupation: string; 5 | introduction_part_1: string; 6 | introduction_highlight_part_1: string; 7 | introduction_part_2: string; 8 | introduction_highlight_part_2: string; 9 | introduction_part_3: string; 10 | introduction_highlight_part_3: string; 11 | introduction_part_4: string; 12 | introduction_highlight_part_4: string; 13 | }; 14 | 15 | export type { IntroductionTranslation }; 16 | -------------------------------------------------------------------------------- /src/lang/type/skill.type.ts: -------------------------------------------------------------------------------- 1 | type SkillTranslation = { 2 | section_title_prefix: string; 3 | section_title_highlight: string; 4 | section_title_suffix: string; 5 | aws_experiences: string[]; 6 | azure_experiences: string[]; 7 | laravel_experiences: string[]; 8 | livewire_experiences: string[]; 9 | svelte_experiences: string[]; 10 | tailwind_css_experiences: string[]; 11 | terraform_experiences: string[]; 12 | ansible_experiences: string[]; 13 | }; 14 | 15 | export type { SkillTranslation }; 16 | -------------------------------------------------------------------------------- /src/lang/en/index.ts: -------------------------------------------------------------------------------- 1 | import header from './header'; 2 | import introduction from './introduction'; 3 | import project from './project'; 4 | import experience from './experience'; 5 | import skill from './skill'; 6 | import about from './about'; 7 | import footer from './footer'; 8 | import post from './post'; 9 | 10 | export default { 11 | header: header, 12 | introduction: introduction, 13 | project: project, 14 | experience: experience, 15 | skill: skill, 16 | about: about, 17 | footer: footer, 18 | post: post 19 | }; 20 | -------------------------------------------------------------------------------- /src/lang/ja/index.ts: -------------------------------------------------------------------------------- 1 | import header from './header'; 2 | import introduction from './introduction'; 3 | import project from './project'; 4 | import experience from './experience'; 5 | import skill from './skill'; 6 | import about from './about'; 7 | import footer from './footer'; 8 | import post from './post'; 9 | 10 | export default { 11 | header: header, 12 | introduction: introduction, 13 | project: project, 14 | experience: experience, 15 | skill: skill, 16 | about: about, 17 | footer: footer, 18 | post: post 19 | }; 20 | -------------------------------------------------------------------------------- /src/lang/type/experience.type.ts: -------------------------------------------------------------------------------- 1 | type ExperienceTranslation = { 2 | section_title_prefix: string; 3 | section_title_highlight: string; 4 | section_title_suffix: string; 5 | experience_1: { 6 | title: string; 7 | content: string; 8 | }; 9 | experience_2: { 10 | title: string; 11 | content: string; 12 | }; 13 | experience_3: { 14 | title: string; 15 | content: string; 16 | }; 17 | experience_4: { 18 | title: string; 19 | content: string; 20 | }; 21 | }; 22 | 23 | export type { ExperienceTranslation }; 24 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Noto+Sans+SC:wght@100..900&family=Noto+Sans+TC:wght@100..900&display=swap'); 2 | @import 'tailwindcss'; 3 | 4 | @variant dark (&:where(.dark, .dark *)); 5 | 6 | @theme { 7 | --font-sans-roboto: 'Noto Sans TC', 'Noto Sans SC', 'Noto Sans JP', 'Roboto', sans-serif; 8 | --font-caveat: 'Caveat', sans-serif; 9 | 10 | --animate-blink: blink 0.7s infinite; 11 | 12 | @keyframes blink { 13 | 50% { 14 | opacity: 0; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/zh-CN/index.ts: -------------------------------------------------------------------------------- 1 | import header from './header'; 2 | import introduction from './introduction'; 3 | import project from './project'; 4 | import experience from './experience'; 5 | import skill from './skill'; 6 | import about from './about'; 7 | import footer from './footer'; 8 | import post from './post'; 9 | 10 | export default { 11 | header: header, 12 | introduction: introduction, 13 | project: project, 14 | experience: experience, 15 | skill: skill, 16 | about: about, 17 | footer: footer, 18 | post: post 19 | }; 20 | -------------------------------------------------------------------------------- /src/lang/zh-TW/index.ts: -------------------------------------------------------------------------------- 1 | import header from './header'; 2 | import introduction from './introduction'; 3 | import project from './project'; 4 | import experience from './experience'; 5 | import skill from './skill'; 6 | import about from './about'; 7 | import footer from './footer'; 8 | import post from './post'; 9 | 10 | export default { 11 | header: header, 12 | introduction: introduction, 13 | project: project, 14 | experience: experience, 15 | skill: skill, 16 | about: about, 17 | footer: footer, 18 | post: post 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/components/icons/Terraform.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /src/lib/components/icons/MusicNoteBeamed.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/lang/en/introduction.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | i_am: "Hi! I'm", 3 | a: 'A', 4 | occupation: 'Engineer', 5 | introduction_part_1: 'Experienced backend developer who is skilled in ', 6 | introduction_highlight_part_1: 'operations', 7 | introduction_part_2: ' and ', 8 | introduction_highlight_part_2: 'cloud services', 9 | introduction_part_3: '. Passionate about exploring both ', 10 | introduction_highlight_part_3: 'frontend and backend', 11 | introduction_part_4: 12 | 'technologies in my free time. My work style is adaptable, but I strive for precision and rigor.', 13 | introduction_highlight_part_4: '' 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/components/icons/Moon.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/components/icons/Sun.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/components/icons/Terminal.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/icons/Easel.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/lang/zh-CN/about.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '想知道更多', 3 | section_title_highlight: '关于我', 4 | section_title_suffix: '的事情吗?', 5 | I_am_also: '我其实也是...', 6 | strengths: [ 7 | { 8 | name: '一位全栈工程师', 9 | description: 10 | '下班后,我喜欢探索与工作完全不同的技术领域。我对前端、后端甚至数据库都有涉猎。虽然这些领域看似与我的本职工作毫不相关,但通过这些探索,我能更深刻地理解开发者的需求,并为他们量身打造高效的工作流程和完善的服务架构。' 11 | }, 12 | { 13 | name: '一位很好的倾听者与沟通者', 14 | description: 15 | '我认为与人沟通,并倾听他人的需求,是团队走向成功的必要条件。通过良好的沟通,我们不仅能从中学习更多新知,还能更深入地了解彼此,从而更有效地合作。' 16 | }, 17 | { 18 | name: '一位热爱户外运动的阳光宅', 19 | description: 20 | '躺在沙发上懒洋洋地看动画或电影,坐在电脑前思考这个 Bug 是怎么来的,都是我放松身心的方式。但除了宅在家里,我也喜欢走到户外。享受阳光、跑步、骑自行车和露营都是我热爱的户外活动。' 21 | } 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /src/lang/zh-TW/about.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '想知道更多', 3 | section_title_highlight: '關於我', 4 | section_title_suffix: '的事情嗎?', 5 | I_am_also: '我其實也是...', 6 | strengths: [ 7 | { 8 | name: '一位全端工程師', 9 | description: 10 | '下班後,我喜歡探索與工作完全不同的技術領域。我對前端、後端甚至資料庫都有涉獵。雖然這些領域看似與我的本職工作毫不相干,但透過這些探索,我能更深刻的理解開發者的需求,並為他們量身打造出高效的工作流程和完善的服務架構。' 11 | }, 12 | { 13 | name: '一位很好的傾聽者與溝通者', 14 | description: 15 | '我認為與人溝通,並傾聽他人的需求,是團隊走向成功的必要條件。透過良好的溝通,我們不只能從中學習更多新知,還能更深入的了解彼此,進而更有效的合作。' 16 | }, 17 | { 18 | name: '一位喜愛戶外運動的陽光宅', 19 | description: 20 | '躺在沙發上慵懶的看動畫或是電影,坐在電腦前思考這個 Bug 是怎麼來的,都是我放鬆身心的方式。但除了宅在家裡,我也喜歡走出戶外。享受陽光、跑步、騎自行車和露營都是我熱愛的戶外活動。' 21 | } 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /src/lib/components/icons/Language.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/components/icons/Lightbulb.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 18 | // from the referenced tsconfig.json - TypeScript does not merge them in 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/components/icons/Cloud.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/components/icons/FolderOpen.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/components/icons/FileEarmarkRichText.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/icons/Ansible.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /src/lib/components/icons/Floppy.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/components/icons/ChatHeart.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/lang/type/index.type.ts: -------------------------------------------------------------------------------- 1 | import type { HeaderTranslation } from './header.type'; 2 | import type { IntroductionTranslation } from './introduction.type'; 3 | import type { ProjectTranslation } from './project.type'; 4 | import type { ExperienceTranslation } from './experience.type'; 5 | import type { SkillTranslation } from './skill.type'; 6 | import type { AboutTranslation } from './about.type'; 7 | import type { FooterTranslation } from './footer.type'; 8 | import type { PostTranslation } from './post.type'; 9 | 10 | type Translation = { 11 | header: HeaderTranslation; 12 | introduction: IntroductionTranslation; 13 | project: ProjectTranslation; 14 | experience: ExperienceTranslation; 15 | skill: SkillTranslation; 16 | about: AboutTranslation; 17 | footer: FooterTranslation; 18 | post: PostTranslation; 19 | }; 20 | 21 | export type { Translation }; 22 | -------------------------------------------------------------------------------- /src/lib/components/icons/ClipboardCheck.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /src/lang/ja/about.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: '私のこと', 4 | section_title_suffix: 'をもっと知りたい?', 5 | I_am_also: '実は私は...', 6 | strengths: [ 7 | { 8 | name: 'フルスタックエンジニア', 9 | description: 10 | '仕事が終わった後、仕事とは全く違うテクノロジーの分野を探求するのが好きです。 フロントエンド、バックエンド、そしてデータベースにも渉猟したことがある。 これらの分野は仕事とは無関係に見えるかもしれませんが、こうした探求を通して、開発者のニーズを深く理解し、彼らのニーズを踏まえた効率的なワークフローや完備したサービスアーキテクチャを作成することができるようになります。' 11 | }, 12 | { 13 | name: '聞き上手でコミュニケーション上手', 14 | description: 15 | '他者とコミュニケーションをとり、彼らのニーズに耳を傾けることは、チームの成功に不可欠だと思っています。 良好なコミュニケーションは、チームメンバーがより多くを学ぶことを可能にするだけではなく、互いをよりよく理解し、より効果的に協力することを可能にする。' 16 | }, 17 | { 18 | name: '陽気なアウトドアオタク', 19 | description: 20 | 'ソファでのんびりアニメや映画を見たり、パソコンの前に座ってバグの解決方法を考えたりするのが私のリラックス方法だ。 でも、家に閉じこもっている以外に、アウトドア・アクティビティも大好きだ。 日光浴、ランニング、サイクリングとキャンピングは私のお気に入りのアウトドア活動です。' 21 | } 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /src/lib/components/icons/GlobeAmericas.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/components/icons/PersonWalking.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 25 | 26 | 27 |
28 | 29 | 30 |
31 | {@render children?.()} 32 |
33 |
34 | -------------------------------------------------------------------------------- /src/lib/components/icons/JournalText.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/components/icons/BatteryCharging.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/components/icons/Bug.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import tailwindcss from '@tailwindcss/vite'; 2 | import { sveltekit } from '@sveltejs/kit/vite'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [tailwindcss(), sveltekit()], 7 | test: { 8 | expect: { requireAssertions: true }, 9 | projects: [ 10 | { 11 | extends: './vite.config.ts', 12 | test: { 13 | name: 'client', 14 | environment: 'browser', 15 | browser: { 16 | enabled: true, 17 | provider: 'playwright', 18 | instances: [{ browser: 'chromium' }] 19 | }, 20 | include: ['src/**/*.svelte.{test,spec}.{js,ts}'], 21 | exclude: ['src/lib/server/**'], 22 | setupFiles: ['./vitest-setup-client.ts'] 23 | } 24 | }, 25 | { 26 | extends: './vite.config.ts', 27 | test: { 28 | name: 'server', 29 | environment: 'node', 30 | include: ['src/**/*.{test,spec}.{js,ts}'], 31 | exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'] 32 | } 33 | } 34 | ] 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /src/lib/components/icons/TailwindCss.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /src/lib/components/icons/Cpu.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/lang/zh-CN/experience.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: '过往的经历', 4 | section_title_suffix: '成就现在的我', 5 | experience_1: { 6 | title: '新人初出茅庐,进入资安领域', 7 | content: 8 | '刚踏入职场时,我在因缘际会下进入了资安领域。' + 9 | '工作上除了协助资安工具的开发,我还协助了资安课程的设计、靶机环境的准备与教材的编写。' + 10 | '在这份工作中,我学习到很多资安相关的基础知识,也成功拿到了一张基础的 CEH 资安认证。' 11 | }, 12 | experience_2: { 13 | title: '接触后端开发,深耕后端领域', 14 | content: 15 | '因为在工作期间对网页开发产生了相当浓厚的兴趣,我决定成为后端工程师。' + 16 | '虽然我起步相对较晚,但在同事的帮助与自身的努力下,我逐渐成长为一位合格的后端工程师,并渐渐地在后端领域积累了丰富的经验。' 17 | }, 18 | experience_3: { 19 | title: '学习前端技能并开始写博客,记录并分享所学的技术', 20 | content: 21 | '在前辈的建议下,我花费了数月来开发自己的博客,并将其搭建在云端服务上。' + 22 | '我将自己对技术的热情以及在工作中遇到的挑战,通过文章分享在博客上,希望能帮助到遇到相同问题的人。' + 23 | '写文章让我受益良多,不仅帮助自己加深记忆,也让我结识了许多志同道合的伙伴。' 24 | }, 25 | experience_4: { 26 | title: '转换跑道,学习成为一位 DevOps 工程师', 27 | content: 28 | '因为开发博客,我接触到了云端服务与各种运维工具,并从中发现了 DevOps 的魅力。' + 29 | '如何确保代码的质量并自动部署到正式环境上稳定运行,对我来说是一件非常有趣的事情。' + 30 | '在某次因缘际会之下,我拿到了 DevOps 工程师的 Offer,于是决定转换跑道,成为一位 DevOps 工程师。' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/lang/zh-TW/experience.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: '過往的經歷', 4 | section_title_suffix: '成就現在的我', 5 | experience_1: { 6 | title: '新人初出茅廬,進入資安領域', 7 | content: 8 | '剛踏入職場時,我在因緣際會下進入了資安領域。' + 9 | '工作上除了協助資安工具的開發,我還協助了資安課程的設計,靶機環境的準備與教材的編寫。' + 10 | '這在份工作中,我學習到很多資安相關的基礎知識,也成功拿到一張基礎的 CEH 資安認證。' 11 | }, 12 | experience_2: { 13 | title: '接觸後端開發,深耕後端領域', 14 | content: 15 | '因為在工作期間對網頁開發產生了相當濃厚的興趣,我決定成為後端工程師。' + 16 | '雖然我起步相對較晚,但在同事的幫助與自身的努力下,我逐漸成長為一位合格的後端工程師,並漸漸的在後端領域積累了豐富的經驗。' 17 | }, 18 | experience_3: { 19 | title: '學習前端技能並開始寫部落格,紀錄並分享所學的技術', 20 | content: 21 | '在前輩的建議下,我花費了數月來開發自己的部落格,並將其搭建在雲端服務上。' + 22 | '我將自己對技術的熱情以及在工作中遇到的挑戰,透過文章分享在部落格上,希望能幫助到跟我遇到相同問題的人。' + 23 | '寫文章讓我受益良多,不只幫助自己加深記憶,也讓我結識了許多志同道合的夥伴。' 24 | }, 25 | experience_4: { 26 | title: '轉換跑道,學習成為一位 DevOps 工程師', 27 | content: 28 | '因為開發部落格,我接觸到了雲端服務與各種維運工具,並從中發現了 DevOps 魅力。' + 29 | '如何確保程式碼的品質並自動部署到正式環境上穩定運行,對我來說是一件非常有趣的事情。' + 30 | '在某次因緣際會之下,我拿到了 DevOps 工程師的 Offer,於是決定轉換跑道,成為一位 DevOps 工程師。' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import prettier from 'eslint-config-prettier'; 2 | import js from '@eslint/js'; 3 | import { includeIgnoreFile } from '@eslint/compat'; 4 | import svelte from 'eslint-plugin-svelte'; 5 | import globals from 'globals'; 6 | import { fileURLToPath } from 'node:url'; 7 | import ts from 'typescript-eslint'; 8 | import svelteConfig from './svelte.config.js'; 9 | 10 | const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); 11 | 12 | export default ts.config( 13 | includeIgnoreFile(gitignorePath), 14 | js.configs.recommended, 15 | ...ts.configs.recommended, 16 | ...svelte.configs.recommended, 17 | prettier, 18 | ...svelte.configs.prettier, 19 | { 20 | languageOptions: { 21 | globals: { ...globals.browser, ...globals.node } 22 | }, 23 | rules: { 'no-undef': 'off' } 24 | }, 25 | { 26 | files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], 27 | languageOptions: { 28 | parserOptions: { 29 | projectService: true, 30 | extraFileExtensions: ['.svelte'], 31 | parser: ts.parser, 32 | svelteConfig 33 | } 34 | } 35 | } 36 | ); 37 | -------------------------------------------------------------------------------- /src/lib/components/icons/Nginx.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | file_type_nginx 18 | -------------------------------------------------------------------------------- /src/lib/components/icons/K3s.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 16 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /src/lang/ja/experience.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: '過去の経験', 4 | section_title_suffix: 'が今の私を作っている', 5 | experience_1: { 6 | title: '新人として情報セキュリティの分野へ', 7 | content: 8 | '就職したばかりの頃、縁あって情報セキュリティの分野に足を踏み入れました。' + 9 | '情報セキュリティツールの開発協力に加え、情報セキュリティ講座の設計、仮想イメージの整備、教材作成などを手伝いました。' + 10 | 'この仕事で、情報セキュリティに関する基礎知識をたくさん学び、CEH の基礎資格を取得することができました。' 11 | }, 12 | experience_2: { 13 | title: 'バックエンド開発に携わり、その分野に専念する', 14 | content: 15 | 'バックエンドエンジニアになろうと思ったのは、在職中に Web 開発に興味を持ったからです。' + 16 | '始めるのが遅かったのですが、同僚の助けや自分の努力もあり、徐々に適格なバックエンドエンジニアになり、多くの経験を積めました。' 17 | }, 18 | experience_3: { 19 | title: 'フロントエンドの技術を学び、学んだ技術を記録し共有するためにブログを書き始めました', 20 | content: 21 | '在先輩のアドバイスで、数ヶ月かけて自分のブログを開発し、クラウド上に構築しました。' + 22 | 'ブログの記事を通じて、技術への情熱や仕事で遭遇した課題を共有し、自分と同じ問題に遭遇した人たちの役に立ちたいと思った。' + 23 | '記事を書くことで、自分の記憶を定着させるだけでなく、反りが合う人たちと出会うことができ、大いに役立っている。' 24 | }, 25 | experience_4: { 26 | title: 'DevOps エンジニアに転職ための学習', 27 | content: 28 | 'ブログ開発を通じて、クラウドサービスや様々な保守運用ツールに触れ、DevOps の魅力を感じました。' + 29 | 'コードの品質を担保し、自動的に正式な環境にデプロイして安定稼働させる方法は、私にとって非常に興味深いものでした。' + 30 | 'ひょんなことから DevOps エンジニアのオファーをいただき、DevOps エンジニアへの転職をすることに決めました。' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/lib/components/layouts/Footer.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
19 | {translation.content_prefix} 20 | 21 | SvelteKit 26 | {translation.and} 27 | 28 | Tailwind CSS 33 | {translation.content_suffix} 34 |
35 | -------------------------------------------------------------------------------- /src/lib/components/Step.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 |
23 | {@render icon?.()} 24 |
25 | 26 |

{name}

27 | 28 | {@render content?.()} 29 | 30 |
31 |

34 | {forwardText} → 35 |

36 |
37 |
38 | -------------------------------------------------------------------------------- /e2e/page.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test'; 2 | 3 | test('user can visit root page', async ({ page }) => { 4 | const response = await page.request.get('/en'); 5 | await expect(response).toBeOK(); 6 | }); 7 | 8 | test('index page has expected h1', async ({ page }) => { 9 | await page.goto('/en'); 10 | await expect(page.getByRole('heading', { name: 'Allen Jiang' })).toBeVisible(); 11 | }); 12 | 13 | test('visitor can see introduction section', async ({ page }) => { 14 | await page.goto('/en'); 15 | const section = await page.$('#introduction'); 16 | expect(section).not.toBeNull(); 17 | }); 18 | 19 | test('visitor can see projects section', async ({ page }) => { 20 | await page.goto('/en'); 21 | const section = await page.$('#project'); 22 | expect(section).not.toBeNull(); 23 | }); 24 | 25 | test('visitor can see experience section', async ({ page }) => { 26 | await page.goto('/en'); 27 | const section = await page.$('#experience'); 28 | expect(section).not.toBeNull(); 29 | }); 30 | 31 | test('visitor can see tech-stack section', async ({ page }) => { 32 | await page.goto('/en'); 33 | const section = await page.$('#skill'); 34 | expect(section).not.toBeNull(); 35 | }); 36 | 37 | test('visitor can see about section', async ({ page }) => { 38 | await page.goto('/en'); 39 | const section = await page.$('#about'); 40 | expect(section).not.toBeNull(); 41 | }); 42 | -------------------------------------------------------------------------------- /src/lang/en/about.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: 'Want to Know More', 3 | section_title_highlight: 'About Me', 4 | section_title_suffix: '?', 5 | I_am_also: 'In fact, I am also...', 6 | strengths: [ 7 | { 8 | name: 'A Full Stack Developer', 9 | description: 10 | 'After work, I like to explore technology areas that are completely different from my job. ' + 11 | "I've dabbled in frontend, backend, and even databases. " + 12 | 'Although these areas may seem unrelated to my job, ' + 13 | "through these explorations I've been able to gain a deeper understanding of developers' needs " + 14 | 'and create efficient workflows and service architectures tailored to their needs.' 15 | }, 16 | { 17 | name: 'A Good Listener and Communicator', 18 | description: 19 | 'I believe that communicating with others and listening to their needs is essential to the success of a team. ' + 20 | 'Through good communication, we can not only learn more from each other, ' + 21 | 'but also understand how to work together more effectively.' 22 | }, 23 | { 24 | name: 'A lover of outdoor activities', 25 | description: 26 | 'Lounging on the couch, watching an animation or a movie, ' + 27 | 'or sitting in front of the computer thinking about how to solve the bugs make me relax. ' + 28 | 'But apart from staying at home, ' + 29 | 'I also like to go outdoors. Enjoying the sun, running, biking and camping, these are my favorite outdoor activities.' 30 | } 31 | ] 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio", 3 | "private": true, 4 | "version": "0.0.1", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite dev", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "prepare": "svelte-kit sync || echo ''", 11 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 12 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 13 | "format": "prettier --write .", 14 | "lint": "prettier --check . && eslint .", 15 | "test:unit": "vitest", 16 | "test": "npm run test:unit -- --run && npm run test:e2e", 17 | "test:e2e": "playwright test" 18 | }, 19 | "devDependencies": { 20 | "@eslint/compat": "^1.2.5", 21 | "@eslint/js": "^9.18.0", 22 | "@playwright/test": "^1.49.1", 23 | "@sveltejs/adapter-auto": "^6.0.0", 24 | "@sveltejs/kit": "^2.22.0", 25 | "@sveltejs/vite-plugin-svelte": "^6.0.0", 26 | "@tailwindcss/vite": "^4.0.0", 27 | "@vitest/browser": "^3.2.3", 28 | "eslint": "^9.18.0", 29 | "eslint-config-prettier": "^10.0.1", 30 | "eslint-plugin-svelte": "^3.0.0", 31 | "globals": "^16.0.0", 32 | "playwright": "^1.53.0", 33 | "prettier": "^3.4.2", 34 | "prettier-plugin-svelte": "^3.3.3", 35 | "prettier-plugin-tailwindcss": "^0.6.11", 36 | "svelte": "^5.0.0", 37 | "svelte-check": "^4.0.0", 38 | "tailwindcss": "^4.0.0", 39 | "typescript": "^5.0.0", 40 | "typescript-eslint": "^8.20.0", 41 | "vite": "^7.0.4", 42 | "vitest": "^3.2.3", 43 | "vitest-browser-svelte": "^1.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/routes/[locale]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { PageServerLoad } from './$types'; 2 | import { Locale, Theme } from '$lib/enums'; 3 | import { redirect } from '@sveltejs/kit'; 4 | import type { Post } from '$lib/types'; 5 | 6 | export const load: PageServerLoad = async ({ params, cookies, fetch }) => { 7 | const locales: string[] = Object.values(Locale); 8 | const themes: string[] = Object.values(Theme); 9 | let locale: Locale; 10 | let theme: Theme; 11 | let body: { 12 | data: Post[]; 13 | }; 14 | 15 | if (locales.includes(params.locale ?? '')) { 16 | locale = params.locale as Locale; 17 | cookies.set('locale', params.locale, { path: '/' }); 18 | } else if (locales.includes(cookies.get('locale') ?? '')) { 19 | locale = cookies.get('locale') as Locale; 20 | cookies.set('locale', cookies.get('locale') as Locale, { path: '/' }); 21 | 22 | redirect(301, `/${locale}`); 23 | } else { 24 | cookies.set('locale', Locale.Tw, { path: '/' }); 25 | 26 | redirect(301, `/${Locale.Tw}`); 27 | } 28 | 29 | if (themes.includes(cookies.get('theme') ?? '')) { 30 | theme = cookies.get('theme') as Theme; 31 | } else { 32 | cookies.set('theme', Theme.Light, { path: '/', httpOnly: false }); 33 | theme = Theme.Light; 34 | } 35 | 36 | const response = await fetch('https://docfunc.com/api/posts'); 37 | 38 | if (response.ok) { 39 | body = await response.json(); 40 | } else { 41 | body = { data: [] }; 42 | } 43 | 44 | return { 45 | locale: locale, 46 | theme: theme, 47 | posts: body.data 48 | }; 49 | }; 50 | -------------------------------------------------------------------------------- /src/lang/type/project.type.ts: -------------------------------------------------------------------------------- 1 | type ProjectTranslation = { 2 | section_title_prefix: string; 3 | section_title_highlight: string; 4 | section_title_suffix: string; 5 | blog: { 6 | name: string; 7 | description_part_1: string; 8 | description_highlight_part_1: string; 9 | description_part_2: string; 10 | description_highlight_part_2: string; 11 | description_part_3: string; 12 | description_highlight_part_3: string; 13 | description_part_4: string; 14 | description_highlight_part_4: string; 15 | description_part_5: string; 16 | description_highlight_part_5: string; 17 | }; 18 | note: { 19 | name: string; 20 | description_part_1: string; 21 | description_highlight_part_1: string; 22 | description_part_2: string; 23 | description_highlight_part_2: string; 24 | description_part_3: string; 25 | description_highlight_part_3: string; 26 | description_part_4: string; 27 | description_highlight_part_4: string; 28 | description_part_5: string; 29 | description_highlight_part_5: string; 30 | }; 31 | slide: { 32 | name: string; 33 | description_part_1: string; 34 | description_highlight_part_1: string; 35 | description_part_2: string; 36 | description_highlight_part_2: string; 37 | description_part_3: string; 38 | description_highlight_part_3: string; 39 | description_part_4: string; 40 | description_highlight_part_4: string; 41 | description_part_5: string; 42 | description_highlight_part_5: string; 43 | }; 44 | go: string; 45 | }; 46 | 47 | export type { ProjectTranslation }; 48 | -------------------------------------------------------------------------------- /src/lang/README.md: -------------------------------------------------------------------------------- 1 | # Translations 2 | 3 | This folder is used to store text files for various translations. 4 | 5 | You can check `lang/type` to know the translation data structure for each section. 6 | 7 | For example, this is the introduction section translation data structure. 8 | 9 | ```typescript 10 | // lang/type/introduction.type.ts 11 | type IntroductionTranslation = { 12 | i_am: string; 13 | a: string; 14 | occupation: string; 15 | introduction_part_1: string; 16 | introduction_highlight_part_1: string; 17 | introduction_part_2: string; 18 | introduction_highlight_part_2: string; 19 | introduction_part_3: string; 20 | introduction_highlight_part_3: string; 21 | introduction_part_4: string; 22 | introduction_highlight_part_4: string; 23 | }; 24 | 25 | export type { IntroductionTranslation }; 26 | ``` 27 | 28 | There should be a `introduction.ts` in each locale translation folder. 29 | 30 | ```typescript 31 | // lang/en/introduction.ts 32 | export default { 33 | i_am: "Hi! I'm", 34 | a: 'A', 35 | occupation: 'Engineer', 36 | introduction_part_1: 'Experienced backend developer who is skilled in ', 37 | introduction_highlight_part_1: 'operations', 38 | introduction_part_2: ' and ', 39 | introduction_highlight_part_2: 'cloud services', 40 | introduction_part_3: '. Passionate about exploring both ', 41 | introduction_highlight_part_3: 'frontend and backend', 42 | introduction_part_4: 43 | 'technologies in my free time. My work style is adaptable, but I strive for precision and rigor.', 44 | introduction_highlight_part_4: '' 45 | }; 46 | ``` 47 | 48 | -------------------------------------------------------------------------------- /src/lang/zh-CN/project.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '看看我做了哪些', 3 | section_title_highlight: '有趣的事情', 4 | section_title_suffix: '', 5 | blog: { 6 | name: '博客', 7 | description_part_1: '这是我用 ', 8 | description_highlight_part_1: 'Tailwind CSS', 9 | description_part_2: '、', 10 | description_highlight_part_2: 'Alpine.js', 11 | description_part_3: '、', 12 | description_highlight_part_3: 'Laravel', 13 | description_part_4: ' 与 ', 14 | description_highlight_part_4: 'Livewire', 15 | description_part_5: 16 | ' 所打造的简易博客,用来记录我在生活中的大小事。' + 17 | '无论是工作上遇到的技术难题、最近玩过的电子游戏,或是生活中发生的趣事,都会写在这里与大家分享。', 18 | description_highlight_part_5: '' 19 | }, 20 | note: { 21 | name: '笔记', 22 | description_part_1: '', 23 | description_highlight_part_1: '这里是我用来记录学习新技术时的心得与笔记', 24 | description_part_2: 25 | '无论是学习新的运维技术,或是研究新的编程语言,都会在这里记录下来。希望能够帮助到有需要的人,也欢迎大家提供意见与建议!', 26 | description_highlight_part_2: '', 27 | description_part_3: '', 28 | description_highlight_part_3: '', 29 | description_part_4: '', 30 | description_highlight_part_4: '', 31 | description_part_5: '', 32 | description_highlight_part_5: '' 33 | }, 34 | slide: { 35 | name: '简报', 36 | description_part_1: '研究完技术后,', 37 | description_highlight_part_1: '没有什么比与他人分享新知识更有趣的事情了', 38 | description_part_2: 39 | '我时常与一群志同道合的朋友一起分享各种技术与知识,并从中学习。欢迎大家来看看!', 40 | description_highlight_part_2: '', 41 | description_part_3: '', 42 | description_highlight_part_3: '', 43 | description_part_4: '', 44 | description_highlight_part_4: '', 45 | description_part_5: '', 46 | description_highlight_part_5: '' 47 | }, 48 | go: '前往' 49 | }; 50 | -------------------------------------------------------------------------------- /src/lang/zh-TW/project.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '看看我做了哪些', 3 | section_title_highlight: '有趣的事情', 4 | section_title_suffix: '', 5 | blog: { 6 | name: '部落格', 7 | description_part_1: '這是我用 ', 8 | description_highlight_part_1: 'Tailwind CSS', 9 | description_part_2: '、', 10 | description_highlight_part_2: 'Alpine.js', 11 | description_part_3: '、', 12 | description_highlight_part_3: 'Laravel', 13 | description_part_4: ' 與 ', 14 | description_highlight_part_4: 'Livewire', 15 | description_part_5: 16 | '所打造的簡易部落格,用來記錄我在生活中的大小事。' + 17 | '無論是工作上遇到的技術難題、最近玩過的電玩遊戲,或是生活中發生的趣事,都會寫在這邊與大家分享。', 18 | description_highlight_part_5: '' 19 | }, 20 | note: { 21 | name: '筆記', 22 | description_part_1: '', 23 | description_highlight_part_1: '這裡是我用來記錄學習新技術時的心得與筆記', 24 | description_part_2: 25 | '無論是學習新的維運技術,或是研究新的程式語言,都會在這裡記錄下來。希望能夠幫助到有需要的人,也歡迎大家提供意見與建議!', 26 | description_highlight_part_2: '', 27 | description_part_3: '', 28 | description_highlight_part_3: '', 29 | description_part_4: '', 30 | description_highlight_part_4: '', 31 | description_part_5: '', 32 | description_highlight_part_5: '' 33 | }, 34 | slide: { 35 | name: '簡報', 36 | description_part_1: '研究完技術後,', 37 | description_highlight_part_1: '沒有什麼比與他人分享新知識更有趣的事情了', 38 | description_part_2: 39 | '我時常與一群志同道合的朋友一起分享各種技術與知識,並從中學習。歡迎大家來看看!', 40 | description_highlight_part_2: '', 41 | description_part_3: '', 42 | description_highlight_part_3: '', 43 | description_part_4: '', 44 | description_highlight_part_4: '', 45 | description_part_5: '', 46 | description_highlight_part_5: '' 47 | }, 48 | go: '前往' 49 | }; 50 | -------------------------------------------------------------------------------- /src/lib/components/icons/GoogleCloudPlatform.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 33 | 38 | 44 | 49 | 55 | 56 | -------------------------------------------------------------------------------- /src/lang/ja/project.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '私がやった', 3 | section_title_highlight: '面白いプロジェクト', 4 | section_title_suffix: 'を見よう', 5 | blog: { 6 | name: 'ブログ', 7 | description_part_1: 'これは私が ', 8 | description_highlight_part_1: 'Tailwind CSS', 9 | description_part_2: '、', 10 | description_highlight_part_2: 'Alpine.js', 11 | description_part_3: '、', 12 | description_highlight_part_3: 'Laravel', 13 | description_part_4: ' と ', 14 | description_highlight_part_4: 'Livewire', 15 | description_part_5: 16 | ' で作ったシンプルなブログで、私の近況を記録しています。' + 17 | '仕事で遭遇した技術的な問題や、最近プレイしたビデオゲーム、人生で起こった面白い話など、どんなことでもここにシェアしています。', 18 | description_highlight_part_5: '' 19 | }, 20 | note: { 21 | name: 'メモ', 22 | description_part_1: '', 23 | description_highlight_part_1: 'ここは、私が学んでいる新しい技術についてメモする場所です。', 24 | description_part_2: 25 | '新しい運用技術の習得や、新しいプログラミング言語の研究など、ここに記録しています。 困っている人たちの助けになれば幸いです。コメントや提案もお待ちしています!', 26 | description_highlight_part_2: '', 27 | description_part_3: '', 28 | description_highlight_part_3: '', 29 | description_part_4: '', 30 | description_highlight_part_4: '', 31 | description_part_5: '', 32 | description_highlight_part_5: '' 33 | }, 34 | slide: { 35 | name: 'スライド', 36 | description_part_1: '技術を研究した後、', 37 | description_highlight_part_1: '新しい知識を他の人とシェアすることほど楽しいことはありません。', 38 | description_part_2: 39 | '私はよく、同じ志を持つ友人たちと様々な技術や知識を共有し、彼らから学んでいます。 ぜひご覧ください!', 40 | description_highlight_part_2: '', 41 | description_part_3: '', 42 | description_highlight_part_3: '', 43 | description_part_4: '', 44 | description_highlight_part_4: '', 45 | description_part_5: '', 46 | description_highlight_part_5: '' 47 | }, 48 | go: '続く' 49 | }; 50 | -------------------------------------------------------------------------------- /src/lib/components/icons/HandThumbsUp.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/components/icons/RocketTakeoff.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/components/icons/Svelte.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 21 | 25 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/lib/components/icons/Controller.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/CircularProgressBar.svelte: -------------------------------------------------------------------------------- 1 | 43 | 44 |
51 |
55 | {@render children?.()} 56 |
57 |
58 | 59 | 74 | -------------------------------------------------------------------------------- /src/lib/components/icons/Php.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 15 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/components/Toggle.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 62 | -------------------------------------------------------------------------------- /src/lang/zh-CN/skill.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '这些是我', 3 | section_title_highlight: '使用过的技术', 4 | section_title_suffix: '', 5 | aws_experiences: [ 6 | '拥有 AWS Solution Architecture Associate 认证', 7 | '使用 EC2、RDS 与 ElastiCache 搭建 LEMP 来运行自己的 Laravel 应用程序', 8 | '使用 EC2 搭建简易的 K3s 环境,运行各种自己的服务容器', 9 | '通过 Saving Plan 与 Reserved Instance 的规划来降低机器租用的成本', 10 | '使用 Lambda 运行自己的 Laravel 应用程序,并分别使用 DynamoDB 与 SQS 作为缓存与队列', 11 | '使用 Athena 与 Glue 搭建系统日志查询功能,搜索位于 S3 上的日志' 12 | ], 13 | azure_experiences: [ 14 | '考取 Azure Developer Associate 认证,协助公司取得微软金级伙伴认证', 15 | '通过 Fluent Bit 将数据上传至 Azure Storage Account,并通过 Azure Data Explorer 对数据进行分析' 16 | ], 17 | laravel_experiences: [ 18 | '使用 Laravel 开发自己的博客', 19 | '与 Algolia 整合实现博客的文章搜索功能', 20 | '使用 Laravel Octane 加速网站的响应速度', 21 | '与 ElasticSearch 整合以提升百万笔公司商业数据的搜索速度', 22 | '与 SendGrid 整合实现邮件发送服务', 23 | '多租户架构 (Multi-Tenancy) 系统的功能开发与维护', 24 | '与团队沟通后,协力导入 Laravel Pint 来统一团队的代码编写风格', 25 | '使用 Pest 编写各种功能测试与单元测试' 26 | ], 27 | livewire_experiences: [ 28 | '使用 Livewire 开发自己的博客前端,并实现 SPA 优化用户体验', 29 | '使用 Livewire 开发留言系统,用户可以使用 Markdown 语法撰写留言并预览渲染结果', 30 | '与 CKEditor 整合,实现富文本编辑功能', 31 | '与 Tagify 整合,实现文章标签功能' 32 | ], 33 | svelte_experiences: [ 34 | '使用 SvelteKit 开发自己的个人介绍页', 35 | '使用 SvelteKit 与 Laravel 实作前后端分离的网站' 36 | ], 37 | tailwind_css_experiences: [ 38 | '使用 Tailwind CSS 设计博客的前端样式', 39 | '使用 Tailwind CSS 实作网站的暗黑模式', 40 | '使用 Tailwind CSS 实作响应式网站' 41 | ], 42 | terraform_experiences: [ 43 | '使用 Terraform 管理各种云端资源,如 AWS、Azure 与 GCP', 44 | '使用 Terraform 与 AWS Route 53 管理 DNS SPF 记录,解决 SPF 记录 10 Lookup 上限的问题', 45 | '使用 Terraform 通过操作上百台机器将 S3 上约 100TB 的 Elasticsearch Snapshot 转换成 10 TB 的 Apache Parquet,节省存储成本达 90%,并让用户可以通过 AWS Athena 搜索数据', 46 | '协助公司导入 Terraform 管理资源,发现并清除不再使用的资源,节省成本' 47 | ], 48 | ansible_experiences: [ 49 | '使用 Ansible Playbook 来自动化部署 K3s 集群的流程', 50 | '在公司内部技术分享会上介绍 Ansible 及其使用方法' 51 | ] 52 | }; 53 | -------------------------------------------------------------------------------- /src/lang/zh-TW/skill.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '這些是我', 3 | section_title_highlight: '使用過的技術', 4 | section_title_suffix: '', 5 | aws_experiences: [ 6 | '擁有 AWS Solution Architecture Associate 認證', 7 | '使用 EC2、RDS 與 ElastiCache 搭建 LEMP 來運行自己的 Laravel 應用程式', 8 | '使用 EC2 搭建簡易的 K3s 環境,運行各種自己的服務容器', 9 | '透過 Saving Plan 與 Reserved Instance 的規劃來降低機器租用的成本', 10 | '使用 Lambda 運行自己的 Laravel 應用程式,並分別使用 DynamoDB 與 SQS 來當作快取與佇列', 11 | '使用 Athena 與 Glue 搭建系統日誌查詢功能,搜尋位於 S3 上的日誌' 12 | ], 13 | azure_experiences: [ 14 | '考取 Azure Developer Associate 認證,協助公司取得微軟金級夥伴認證', 15 | '透過 Fluent Bit 將資料上傳至 Azure Storage Account,並透過 Azure Data Explorer對資料進行分析' 16 | ], 17 | laravel_experiences: [ 18 | '使用 Laravel 開發自己的部落格', 19 | '與 Algolia 整合實現部落格的文章搜尋功能', 20 | '使用 Laravel Octane 加速網站的響應速度', 21 | '與 ElasticSearch 整合以提升百萬筆公司商業資料的搜尋速度', 22 | '與 SendGrid 整合實現信件寄送的服務', 23 | '多租戶架構 (Multi-Tenancy) 系統的功能開發與維護', 24 | '與團隊溝通後,協力導入 Laravel Pint 來統一團隊的程式碼撰寫風格', 25 | '使用 Pest 撰寫各種功能測試與單元測試' 26 | ], 27 | livewire_experiences: [ 28 | '使用 Livewire 開發自己的部落格前端,並實現 SPA 優化用戶體驗', 29 | '使用 Livewire 開發留言系統,用戶可以使用 Markdown 語法撰寫留言並預覽渲染結果', 30 | '與 CKEditor 整合,實現富文本編輯的功能', 31 | '與 Tagify 整合,實現文章標籤的功能' 32 | ], 33 | svelte_experiences: [ 34 | '使用 SvelteKit 開發自己的個人介紹頁', 35 | '使用 SvelteKit 與 Laravel 實作前後端分離的網站' 36 | ], 37 | tailwind_css_experiences: [ 38 | '使用 Tailwind CSS 設計部落格的前端樣式', 39 | '使用 Tailwind CSS 實作網站的暗黑模式', 40 | '使用 Tailwind CSS 實作響應式網站' 41 | ], 42 | terraform_experiences: [ 43 | '使用 Terraform 管理各種雲端資源,如 AWS、Azure 與 GCP', 44 | '使用 Terraform 與 AWS Route 53 管理 DNS SPF 記錄,解決 SPF 紀錄 10 Lookup 上限的問題', 45 | '使用 Terraform 透過操作上百台機器將 S3 上約 100TB 的 Elasticsearch Snapshot 轉換成 10 TB 的 Apache Parquet,節省存儲成本達 90% ,並讓用戶可以透過 AWS Athena 搜尋資料', 46 | '協助公司導入 Terraform 管理資源,發現並清除不再使用的資源,節省成本' 47 | ], 48 | ansible_experiences: [ 49 | '使用 Ansible Playbook 來自動化部署 K3s 叢集的流程', 50 | '在公司內部技術分享會介紹 Ansible 與其使用方式' 51 | ] 52 | }; 53 | -------------------------------------------------------------------------------- /src/lib/components/sections/About.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
18 |
19 |
Hey! my friends
20 |

21 | {translation.section_title_prefix} 22 | 25 | 26 | {translation.section_title_highlight} 27 | 28 | 29 | {translation.section_title_suffix} 30 |

31 |
32 |

33 | {translation.I_am_also} 34 |

35 |
36 | {#each translation.strengths as strength, index (index)} 37 |
38 |

39 | {(index + 1).toString().padStart(2, '0')} 40 |

41 |
42 |

43 | {strength.name} 44 |

45 |

{strength.description}

46 |
47 |
48 | {/each} 49 |
50 |
51 | -------------------------------------------------------------------------------- /src/lib/components/icons/Python.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 43 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/lang/en/project.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: 'Check Out These', 3 | section_title_highlight: 'Fun Projects', 4 | section_title_suffix: 'I did', 5 | blog: { 6 | name: 'Blog', 7 | description_part_1: 'This is a simple blog made with ', 8 | description_highlight_part_1: 'Tailwind CSS', 9 | description_part_2: ', ', 10 | description_highlight_part_2: 'Alpine.js', 11 | description_part_3: ', ', 12 | description_highlight_part_3: 'Laravel', 13 | description_part_4: ' and ', 14 | description_highlight_part_4: 'Livewire', 15 | description_part_5: 16 | '. Whether it is technical problems encountered at work, video games recently played, ' + 17 | 'or interesting things that happened in life, they will be written here to share with everyone.', 18 | description_highlight_part_5: '' 19 | }, 20 | note: { 21 | name: 'Note', 22 | description_part_1: '', 23 | description_highlight_part_1: 'Here are the notes I use to record learning', 24 | description_part_2: 25 | 'Whether learning new operation techniques or researching new programming languages, ' + 26 | 'they will be recorded here. ' + 27 | 'I hope it can help those in need, and everyone is welcome to provide comments and suggestions!', 28 | description_highlight_part_2: '', 29 | description_part_3: '', 30 | description_highlight_part_3: '', 31 | description_part_4: '', 32 | description_highlight_part_4: '', 33 | description_part_5: '', 34 | description_highlight_part_5: '' 35 | }, 36 | slide: { 37 | name: 'Slide', 38 | description_part_1: 'After studying, ', 39 | description_highlight_part_1: 40 | 'there is nothing more fun than sharing your new knowledge with others', 41 | description_part_2: 42 | 'I often share various techniques and knowledge friends and learn from them. Everyone is welcome to take a look!', 43 | description_highlight_part_2: '', 44 | description_part_3: '', 45 | description_highlight_part_3: '', 46 | description_part_4: '', 47 | description_highlight_part_4: '', 48 | description_part_5: '', 49 | description_highlight_part_5: '' 50 | }, 51 | go: 'Go' 52 | }; 53 | -------------------------------------------------------------------------------- /src/lib/components/icons/TypeScript.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /src/lang/en/experience.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: 'My Past', 4 | section_title_suffix: 'Made Me', 5 | experience_1: { 6 | title: 'Newcomers to Information Security', 7 | content: 8 | 'My first job was in the information security field. ' + 9 | 'I assisted in the development of security tools, and also assisted in the design of security courses, ' + 10 | 'the preparation of target environment and the writing of teaching materials. ' + 11 | 'I learned a lot of basic knowledge related to information security in this job, and also successfully obtained a basic CEH certification.' 12 | }, 13 | experience_2: { 14 | title: 'Learning Backend Development', 15 | content: 16 | 'I decided to become a backend engineer because of my interest in web development at work.' + 17 | 'Although I started late, with the help of my coworkers and my own efforts, ' + 18 | 'I gradually grew into a qualified backend engineer and accumulated rich experience in backend development.' 19 | }, 20 | experience_3: { 21 | title: 'Learn Frontend Skills and Start Writing Blogs', 22 | content: 23 | 'With the advice of my predecessors, I spent a few months to develop my own blog and build it on cloud service. ' + 24 | 'I shared my passion for technology and the challenges I encountered in my work through articles on my blog, ' + 25 | 'hoping to help people who encountered the same problems as I did. ' + 26 | 'Writing articles has benefited me a lot, not only helping me to deepen my memory, but also allowing me to meet a lot of like-minded partners.' 27 | }, 28 | experience_4: { 29 | title: 'Learn to be a DevOps Engineer', 30 | content: 31 | 'Through developing blogs, I came across cloud services and various maintenance tools, and discovered the fascination of DevOps. ' + 32 | 'How to ensure the quality of the code and automatically deploy it to the official environment to run stably is a very interesting thing to me. ' + 33 | 'By chance, I got an offer to be a DevOps engineer and decided to change my career path to become a DevOps engineer.' 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/lang/ja/skill.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: 'これらは私が', 3 | section_title_highlight: '使ったことのある技術です', 4 | section_title_suffix: '', 5 | aws_experiences: [ 6 | 'AWS Solution Architecture Associate 認定', 7 | 'EC2、RDS と ElastiCache を使用して自作の Laravel アプリケーションを実行するためのLEMPを構築', 8 | 'EC2 を使ってシンプルな K3s 環境を構築し、様々な自作のコンテナを実行', 9 | 'Savings Plans とリザーブドインスタンスサービスでマシンのレンタルコストを削減', 10 | 'Lambda を使用して自作の Laravel アプリケーションを実行し、キャッシュとキューにはそれぞれDynamoDB と SQS を使用', 11 | 'AthenaとGlue を使って、システムログクエリを構築し、S3 にログを検索' 12 | ], 13 | azure_experiences: [ 14 | 'Azure Developer Associate 認定を取得し、当時勤めていた会社のマイクロソフト認定ゴールドパートナー資格取得に貢献しました', 15 | 'Fluent Bit を通じて Azure Storage Account にデータをアップロードし、Azure Data Explorer を通じてデータを分析' 16 | ], 17 | laravel_experiences: [ 18 | 'Laravel を使って独自のブログサイトを開発', 19 | 'Algolia を導入してブログ記事の検索機能を実装', 20 | 'Laravel Octane でウェブサイトの応答速度を高速化', 21 | 'ElasticSearch との統合で、数百万件の商業登記データのクエリスピードを高速化', 22 | 'SendGrid でメール配信サービスを実現', 23 | 'Multi-Tenancy システムの開発とメンテナンス', 24 | 'チームとコミュニケーションをとり、そして Laravel Pint を導入してチームのコードスタイルを統一', 25 | 'Pest を使って様々な機能テストと単体テストを書く' 26 | ], 27 | livewire_experiences: [ 28 | 'Livewire を使って独自のブログフロントエンドを開発し、SPA を実装してユーザーエクスペリエンスを最適化', 29 | 'Livewire を使って、ユーザーが Markdown 構文でコメントを書けるコメントシステムを開発', 30 | 'CKEditor を導入し、リッチテキスト編集を実現', 31 | 'Tagify を導入し、投稿のタグ入力機能を実現' 32 | ], 33 | svelte_experiences: [ 34 | 'SvelteKit を使って自分のプロフィールページを開発', 35 | 'SvelteKit と Laravel を使って、フロントエンドとバックエンドを分離したウェブサイトを実装' 36 | ], 37 | tailwind_css_experiences: [ 38 | 'Tailwind CSS を使って、ブログのフロントエンドのスタイルをデザイン', 39 | 'Tailwind CSS を使ってウェブサイトのダークモードを実装', 40 | 'Tailwind CSS を使ったレスポンシブ Web サイトの実装' 41 | ], 42 | terraform_experiences: [ 43 | 'Terraform で AWS、Azure、GCP など様々なクラウドリソースを管理', 44 | 'Terraform と AWS Route 53 で DNS の SPF レコードを管理し、SPF レコードの 10 Lookup 制限を解決', 45 | 'Terraform を使い、数百台のマシンを操作して S3 上の約 100 TB の Elasticsearch Snapshots を 10 TB の Apache Parquet に変換し、ストレージコストを 90% 節約し、AWS Athena を通してユーザーがデータを検索できるようにした', 46 | '会社に Terraform を導入して、リソースを管理し、使われなくなったリソースを特定して削除し、コストを削減' 47 | ], 48 | ansible_experiences: [ 49 | '使 Ansible Playbook で K3s クラスタのデプロイを自動化する', 50 | '社内の技術共有会で Ansible とその使い方を紹介' 51 | ] 52 | }; 53 | -------------------------------------------------------------------------------- /src/lib/components/sections/Posts.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if posts.length > 0} 18 |
19 |
20 |
Check them out!
21 |

22 | {translation.section_title_prefix} 23 | 26 | 27 | {translation.section_title_highlight} 28 | 29 | 30 | {translation.section_title_suffix} 31 |

32 |
33 | 34 |
35 | {#each posts as post (post.id)} 36 | 57 | {/each} 58 |
59 |
60 | {/if} 61 | -------------------------------------------------------------------------------- /src/lib/components/Modal.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | {#if showModal} 32 | 60 | {/if} 61 | -------------------------------------------------------------------------------- /src/lib/components/icons/Laravel.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /static/assets/particles.json: -------------------------------------------------------------------------------- 1 | { 2 | "particles": { 3 | "number": { 4 | "value": 80, 5 | "density": { 6 | "enable": true, 7 | "value_area": 800 8 | } 9 | }, 10 | "color": { 11 | "value": "#ffffff" 12 | }, 13 | "shape": { 14 | "type": "circle", 15 | "stroke": { 16 | "width": 0, 17 | "color": "#000000" 18 | }, 19 | "polygon": { 20 | "nb_sides": 5 21 | }, 22 | "image": { 23 | "src": "img/github.svg", 24 | "width": 100, 25 | "height": 100 26 | } 27 | }, 28 | "opacity": { 29 | "value": 0.5, 30 | "random": false, 31 | "anim": { 32 | "enable": false, 33 | "speed": 1, 34 | "opacity_min": 0.1, 35 | "sync": false 36 | } 37 | }, 38 | "size": { 39 | "value": 3, 40 | "random": true, 41 | "anim": { 42 | "enable": false, 43 | "speed": 40, 44 | "size_min": 0.1, 45 | "sync": false 46 | } 47 | }, 48 | "line_linked": { 49 | "enable": true, 50 | "distance": 150, 51 | "color": "#ffffff", 52 | "opacity": 0.4, 53 | "width": 1 54 | }, 55 | "move": { 56 | "enable": true, 57 | "speed": 6, 58 | "direction": "none", 59 | "random": false, 60 | "straight": false, 61 | "out_mode": "out", 62 | "bounce": false, 63 | "attract": { 64 | "enable": false, 65 | "rotateX": 600, 66 | "rotateY": 1200 67 | } 68 | } 69 | }, 70 | "interactivity": { 71 | "detect_on": "canvas", 72 | "events": { 73 | "onhover": { 74 | "enable": true, 75 | "mode": "repulse" 76 | }, 77 | "onclick": { 78 | "enable": true, 79 | "mode": "push" 80 | }, 81 | "resize": true 82 | }, 83 | "modes": { 84 | "grab": { 85 | "distance": 400, 86 | "line_linked": { 87 | "opacity": 1 88 | } 89 | }, 90 | "bubble": { 91 | "distance": 400, 92 | "size": 40, 93 | "duration": 2, 94 | "opacity": 8, 95 | "speed": 3 96 | }, 97 | "repulse": { 98 | "distance": 200, 99 | "duration": 0.4 100 | }, 101 | "push": { 102 | "particles_nb": 4 103 | }, 104 | "remove": { 105 | "particles_nb": 2 106 | } 107 | } 108 | }, 109 | "retina_detect": true 110 | } 111 | -------------------------------------------------------------------------------- /src/lib/components/icons/CupHot.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /src/routes/[locale]/+page.svelte: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 | Allen's Portfolio 31 | 32 | 33 | 34 | 35 |
39 |
0, 43 | 'pointer-events-none opacity-0': y <= 0 44 | }} 45 | > 46 | 52 |
53 | 54 |
55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 |
71 |
72 | -------------------------------------------------------------------------------- /src/lib/components/icons/Azure.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 74 | -------------------------------------------------------------------------------- /src/lib/components/icons/GitHubAction.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /src/lib/components/icons/Redis.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 40 | -------------------------------------------------------------------------------- /src/lang/en/skill.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | section_title_prefix: '', 3 | section_title_highlight: 'The Skills', 4 | section_title_suffix: 'I Specialize In', 5 | aws_experiences: [ 6 | 'AWS Solution Architecture Associate certification', 7 | 'Build LEMP to run Laravel application using EC2, RDS and ElastiCache', 8 | 'Built a simple K3s environment using EC2 to run a variety of self-packaged containers', 9 | 'Reduce machine rental costs with Saving Plan and Reserved Instance planning', 10 | 'Running my Laravel application using Lambda, with DynamoDB and SQS for caching and queuing respectively', 11 | 'Built a system log query using Athena and Glue to search for logs on S3' 12 | ], 13 | azure_experiences: [ 14 | 'Obtain Azure Developer Associate certification to help company achieve Microsoft Gold Partner certification', 15 | 'Upload data to Azure Storage Account via Fluent Bit and analyze data via Azure Data Explorer' 16 | ], 17 | laravel_experiences: [ 18 | 'Developing my blog with Laravel', 19 | 'Integrate with Algolia to make blog posts searchable', 20 | 'Accelerating website response with Laravel Octane', 21 | 'Integrate with ElasticSearch to speed up searches of millions of company business data', 22 | 'Integration with SendGrid for mail delivery service', 23 | 'Multi-Tenancy system development and maintenance', 24 | "Communicate with the team and implement Laravel Pint to standardize the team's code writing style", 25 | 'Use Pest to write various feature e2e and unit e2e' 26 | ], 27 | livewire_experiences: [ 28 | 'Use Livewire to develop my blog frontend and implement SPA to improve user experience', 29 | 'Developed a comment system using Livewire, which allows users to write messages in Markdown syntax and preview the rendering results', 30 | 'Integrate with CKEditor for rich text editing', 31 | 'Integrate with Tagify for post tagging' 32 | ], 33 | svelte_experiences: [ 34 | 'Using SvelteKit to develop my on portfolio page', 35 | 'Using SvelteKit and Laravel to implement a separate frontend and backend website' 36 | ], 37 | tailwind_css_experiences: [ 38 | 'Using Tailwind CSS to design blog frontend styles', 39 | 'Implementing a dark mode website with Tailwind CSS', 40 | 'Implementing responsive websites with Tailwind CSS' 41 | ], 42 | terraform_experiences: [ 43 | 'Manage various cloud resources such as AWS, Azure and GCP with Terraform', 44 | 'Managed DNS SPF records with Terraform and AWS Route 53, resolving the 10 Lookup limit on SPF records', 45 | 'Used Terraform to convert approximately 100TB of Elasticsearch Snapshots on S3 to 10TB of Apache Parquet by operating hundreds of converting machines, ' + 46 | 'saving up to 90% on storage costs and allowing users to search for data through AWS Athena', 47 | 'Helps companies import Terraform to manage resources, discovering and removing resources that are no longer in use, saving costs' 48 | ], 49 | ansible_experiences: [ 50 | 'Automating the deployment of K3s clusters with Ansible Playbook', 51 | 'Introducing Ansible and how to use it at an in-house tech sharing session' 52 | ] 53 | }; 54 | -------------------------------------------------------------------------------- /src/lib/components/icons/Livewire.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | > 36 | -------------------------------------------------------------------------------- /src/lib/components/icons/MySql.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 23 | 24 | 28 | 32 | 33 | -------------------------------------------------------------------------------- /src/lib/components/icons/Aws.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 18 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /src/lib/components/sections/Introduction.svelte: -------------------------------------------------------------------------------- 1 | 49 | 50 |
54 |
55 |
56 |

57 | {translation.i_am} 58 | 61 | Allen 62 | 63 | 64 |
65 | 66 | {translation.a} 67 | 70 | DevOps 71 | 72 | {translation.occupation} 73 |

74 |
75 | 76 |

77 | {translation.introduction_part_1} 78 | 79 | {translation.introduction_highlight_part_1} 80 | 81 | {translation.introduction_part_2} 82 | 83 | {translation.introduction_highlight_part_2} 84 | 85 | {translation.introduction_part_3} 86 | 87 | {translation.introduction_highlight_part_3} 88 | 89 | {translation.introduction_part_4} 90 | 91 | {translation.introduction_highlight_part_4} 92 | 93 |

94 | 95 |

99 | I Love 100 | {dynamicText} 104 |

105 |
106 | 107 |
108 |
109 | Profile 114 |
117 |
120 |
123 |
126 |
127 |
128 |
129 | 130 | 159 | -------------------------------------------------------------------------------- /src/lib/components/sections/Project.svelte: -------------------------------------------------------------------------------- 1 | 65 | 66 |
67 |
68 |
Love sharing!
69 |

70 | {translation.section_title_prefix} 71 | 74 | 75 | {translation.section_title_highlight} 76 | 77 | 78 | {translation.section_title_suffix} 79 |

80 |
81 | 82 |
83 | {#each Object.entries(steps) as [key, step] (key)} 84 | 85 | {#snippet icon()} 86 | 87 | {/snippet} 88 | 89 | {#snippet content()} 90 | 91 |

92 | {step.descriptionPart1} 93 | 94 | {step.descriptionHighlightPart1} 95 | 96 | {step.descriptionPart2} 97 | 98 | {step.descriptionHighlightPart2} 99 | 100 | {step.descriptionPart3} 101 | 102 | {step.descriptionHighlightPart3} 103 | 104 | {step.descriptionPart4} 105 | 106 | {step.descriptionHighlightPart4} 107 | 108 | {step.descriptionPart5} 109 | 110 | {step.descriptionHighlightPart5} 111 | 112 |

113 | {/snippet} 114 |
115 | {/each} 116 |
117 |
118 | -------------------------------------------------------------------------------- /src/lib/components/sections/Skill.svelte: -------------------------------------------------------------------------------- 1 | 105 | 106 |
107 |
108 |
These stuff are so cool!
109 |

110 | {translation.section_title_prefix} 111 | 114 | 115 | {translation.section_title_highlight} 116 | 117 | 118 | {translation.section_title_suffix} 119 |

120 |
121 | 122 |
123 | {#each Object.entries(skills) as [key, skill] (key)} 124 |
125 | 131 | 138 | 139 | 140 | 141 |

142 | {skill.name} 143 |

144 |
145 | {#each skillExperiences[key] as experience (experience)} 146 |

{experience}

147 | {/each} 148 |
149 |
150 |
151 | {/each} 152 |
153 |
154 | -------------------------------------------------------------------------------- /src/lib/components/Background.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
25 |
26 | 29 | 30 | 33 | 34 | 37 | 38 | 41 | 42 | 45 | 46 | 51 | 54 | 55 | 58 | 59 | 62 | 63 | 66 |
67 | 68 |
69 | 76 | 77 | 80 | 81 | 84 | 85 | 88 | 91 | 92 | 95 | 96 | 99 | 100 | 103 | 104 | 107 | 108 | 111 |
112 |
113 | 114 | 141 | -------------------------------------------------------------------------------- /src/lib/components/layouts/Header.svelte: -------------------------------------------------------------------------------- 1 | 57 | 58 | 59 | 60 |
0, 64 | 'top-0 border-transparent bg-transparent py-6': y <= 0, 65 | 'sticky z-10 mx-2 flex items-center justify-between rounded-2xl border border-solid px-6 duration-200': true 66 | }} 67 | > 68 |

69 | Allen Jiang 70 |

71 | 164 | 169 |
172 |
{translation.check_my_blog} →
173 |
174 |
175 | -------------------------------------------------------------------------------- /src/lib/components/sections/Experience.svelte: -------------------------------------------------------------------------------- 1 | 150 | 151 |
152 |
153 |
From Dev to DevOps
154 |

155 | {translation.section_title_prefix} 156 | 159 | 160 | {translation.section_title_highlight} 161 | 162 | 163 | {translation.section_title_suffix} 164 |

165 |
166 | 167 |
168 | {#each Object.entries(experiences) as [key, experience] (key)} 169 |
170 | 171 |
174 | 178 |
179 | {experienceDescriptions[key].title} 180 |
181 |
182 | 183 |
184 | {experienceDescriptions[key].content} 185 |
186 |
187 | {#each experience.skills as skill (skill.name)} 188 |
191 | 192 | 193 |
196 | 197 | {skill.name} 198 | 199 |
200 |
201 | {/each} 202 |
203 |
204 | {/each} 205 |
206 |
207 | 208 | 220 | -------------------------------------------------------------------------------- /src/lib/components/icons/Rust.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 24 | 28 | 29 | 30 | --------------------------------------------------------------------------------