├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── bug_report_vi.md │ ├── feature_request.md │ └── feature_request_vi.md ├── PULL_REQUEST_TEMPLATE.md ├── funding.yml └── workflows │ ├── ci.yml │ └── update-frontmatter.yml ├── .gitignore ├── .next └── trace ├── CODE_OF_CONDUCT.md ├── CODE_OF_CONDUCT_vi.md ├── CONTRIBUTING.md ├── CONTRIBUTING_vi.md ├── LICENSE.txt ├── Makefile ├── PULL_REQUEST_RULES.md ├── PULL_REQUEST_RULES_vi.md ├── README.md ├── README_WEBSITE.md ├── README_vi.md ├── SUMMARY.md ├── assets ├── diagrams │ └── init.txt └── init.txt ├── changelog.md ├── changelog_vi.md ├── docker-compose.yml ├── docker ├── DATABASE_GUIDE.md ├── DATABASE_GUIDE_vi.md ├── QUICK_START.md ├── QUICK_START_vi.md ├── README.md ├── README_vi.md ├── environments │ ├── cpp │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── csharp │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── databases │ │ ├── .env.example │ │ ├── docker-compose.yml │ │ ├── mongodb │ │ │ ├── docker-compose.yml │ │ │ └── init │ │ │ │ └── 01-sample-data.js │ │ ├── mysql │ │ │ ├── docker-compose.yml │ │ │ └── init │ │ │ │ └── 01-sample-data.sql │ │ ├── postgresql │ │ │ ├── docker-compose.yml │ │ │ └── init │ │ │ │ └── 01-sample-data.sql │ │ ├── redis │ │ │ ├── docker-compose.yml │ │ │ └── redis.conf │ │ └── sqlite │ │ │ ├── Dockerfile │ │ │ ├── docker-compose.yml │ │ │ ├── init.sh │ │ │ └── init.sql │ ├── databricks │ │ └── Dockerfile │ ├── go │ │ └── Dockerfile │ ├── java │ │ ├── Dockerfile │ │ └── entrypoint.sh │ ├── javascript │ │ └── Dockerfile │ ├── php │ │ └── Dockerfile │ ├── python │ │ └── Dockerfile │ ├── ruby │ │ └── Dockerfile │ ├── rust │ │ ├── Dockerfile │ │ └── entrypoint.sh │ └── shell │ │ └── Dockerfile ├── redis │ └── redis.conf ├── run-db-snippet.sh └── run-snippet.sh ├── docs ├── README.md ├── algorithms │ ├── graph-traversal.md │ └── sorting-algorithms.md ├── databases │ └── relational.md ├── design-patterns │ ├── factory.md │ ├── observer.md │ └── singleton.md ├── devops │ └── ci-cd.md ├── linux │ └── bash-scripting.md ├── system-design │ └── microservices.md └── testing │ └── unit-testing.md ├── i18n ├── init.txt └── vi │ ├── README.md │ ├── algorithms │ ├── graph-traversal.md │ └── sorting-algorithms.md │ ├── databases │ └── relational.md │ ├── design-patterns │ ├── factory.md │ ├── observer.md │ └── singleton.md │ ├── devops │ └── ci-cd.md │ ├── linux │ └── bash-scripting.md │ ├── system-design │ └── microservices.md │ └── testing │ └── unit-testing.md ├── package-lock.json ├── package.json ├── snippets ├── algorithms │ ├── graph-traversal │ │ ├── GraphTraversal.cs │ │ ├── GraphTraversal.java │ │ ├── graphTraversal.js │ │ ├── graph_traversal.c │ │ ├── graph_traversal.cpp │ │ ├── graph_traversal.go │ │ ├── graph_traversal.php │ │ ├── graph_traversal.py │ │ ├── graph_traversal.rb │ │ └── graph_traversal.rs │ ├── init.txt │ └── sorting-algorithms │ │ ├── sorting_algorithms.c │ │ ├── sorting_algorithms.cpp │ │ ├── sorting_algorithms.cs │ │ ├── sorting_algorithms.go │ │ ├── sorting_algorithms.java │ │ ├── sorting_algorithms.js │ │ ├── sorting_algorithms.php │ │ ├── sorting_algorithms.py │ │ ├── sorting_algorithms.rb │ │ └── sorting_algorithms.rs ├── databases │ ├── README.md │ ├── README_vi.md │ └── relational │ │ ├── .env.example │ │ ├── relational.cs │ │ └── relational.py ├── design-patterns │ ├── factory │ │ ├── FactoryPattern.cpp │ │ ├── FactoryPattern.cs │ │ ├── FactoryPattern.java │ │ ├── factory_pattern.c │ │ ├── factory_pattern.go │ │ ├── factory_pattern.js │ │ ├── factory_pattern.php │ │ ├── factory_pattern.py │ │ ├── factory_pattern.rb │ │ └── factory_pattern.rs │ ├── observer │ │ ├── ObserverPattern.cpp │ │ ├── ObserverPattern.cs │ │ ├── ObserverPattern.java │ │ ├── observer_pattern.c │ │ ├── observer_pattern.go │ │ ├── observer_pattern.js │ │ ├── observer_pattern.php │ │ ├── observer_pattern.py │ │ ├── observer_pattern.rb │ │ └── observer_pattern.rs │ └── singleton │ │ ├── SingletonPattern.cpp │ │ ├── SingletonPattern.cs │ │ ├── SingletonPattern.java │ │ ├── singleton_pattern.c │ │ ├── singleton_pattern.go │ │ ├── singleton_pattern.js │ │ ├── singleton_pattern.php │ │ ├── singleton_pattern.py │ │ ├── singleton_pattern.rb │ │ └── singleton_pattern.rs ├── devops │ ├── ci-cd │ │ └── ci-cd.sh │ └── init.txt ├── linux │ ├── bash-scripting │ │ └── bash-scripting.sh │ └── init.txt ├── system-design │ ├── init.txt │ └── microservices │ │ └── microservices.js └── testing │ ├── init.txt │ └── unit-testing │ └── unit-testing.js ├── tools ├── README.md ├── check_links.sh ├── generate_summary.py └── update-frontmatter.js └── website ├── .gitignore ├── README.md ├── WEBSITE_README.md ├── components.json ├── eslint.config.mjs ├── next-seo.config.js ├── next.config.js ├── next.config.ts ├── package-lock.json ├── package.json ├── pages └── sitemap.xml.js ├── postcss.config.mjs ├── public ├── file.svg ├── globe.svg ├── next.svg ├── og-image.jpg ├── robots.txt ├── sitemap.txt ├── vercel.svg └── window.svg ├── src ├── app │ ├── [locale] │ │ ├── about │ │ │ └── page.tsx │ │ ├── blog │ │ │ ├── [slug] │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── privacy │ │ │ └── page.tsx │ │ └── terms │ │ │ └── page.tsx │ ├── api │ │ ├── robots.txt │ │ │ └── route.ts │ │ └── sitemap.xml │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components │ ├── blog │ │ ├── category-filter.tsx │ │ ├── code-snippet.tsx │ │ ├── language-switcher.tsx │ │ └── markdown-content.tsx │ ├── layout │ │ ├── footer.tsx │ │ ├── header.tsx │ │ ├── theme-provider.tsx │ │ └── translation-provider.tsx │ ├── search.tsx │ ├── theme-toggle.tsx │ └── ui │ │ ├── accordion.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input.tsx │ │ ├── navigation-menu.tsx │ │ └── sheet.tsx ├── config │ └── site.ts ├── data │ ├── blog-posts.ts │ └── i18n │ │ ├── en │ │ ├── about.json │ │ ├── common.json │ │ └── home.json │ │ └── vi │ │ ├── about.json │ │ ├── common.json │ │ └── home.json ├── lib │ ├── content-mapper.ts │ ├── cookies.ts │ ├── i18n │ │ └── settings.ts │ ├── metadata.ts │ ├── types.ts │ └── utils.ts ├── middleware.ts └── types │ └── index.ts ├── tailwind.config.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings for all files 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # Markdown files 16 | [*.{md,markdown}] 17 | trim_trailing_whitespace = false # Trailing whitespace is significant in Markdown 18 | 19 | # Python files 20 | [*.py] 21 | indent_size = 4 22 | max_line_length = 88 # Black formatter default 23 | 24 | # C# files 25 | [*.cs] 26 | indent_size = 4 27 | csharp_new_line_before_open_brace = all 28 | csharp_space_between_method_declaration_parameter_list_parentheses = false 29 | csharp_space_between_method_call_parameter_list_parentheses = false 30 | csharp_space_after_keywords_in_control_flow_statements = true 31 | csharp_space_between_parentheses = false 32 | 33 | # JavaScript/TypeScript files 34 | [*.{js,ts,jsx,tsx}] 35 | quote_type = single 36 | 37 | # JSON files 38 | [*.json] 39 | insert_final_newline = false 40 | 41 | # YAML files 42 | [*.{yml,yaml}] 43 | indent_size = 2 44 | 45 | # Shell scripts 46 | [*.sh] 47 | indent_size = 2 48 | 49 | # HTML, CSS, SCSS files 50 | [*.{html,css,scss}] 51 | indent_size = 2 52 | 53 | # Java files 54 | [*.java] 55 | indent_size = 4 56 | 57 | # Go files 58 | [*.go] 59 | indent_style = tab 60 | indent_size = 4 61 | 62 | # Makefiles 63 | [Makefile] 64 | indent_style = tab 65 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | 29 | **Possible solution** 30 | If you have suggestions on how to fix the issue, please describe them here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Báo cáo lỗi 3 | about: Tạo một báo cáo lỗi để giúp chúng tôi cải thiện 4 | title: '[BUG] ' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Mô tả lỗi** 11 | Mô tả ngắn gọn và rõ ràng về lỗi đang gặp phải. 12 | 13 | **Cách để tái tạo lỗi** 14 | Các bước để tái hiện hành vi lỗi: 15 | 1. Truy cập vào '...' 16 | 2. Nhấn vào '...' 17 | 3. Cuộn xuống tới '...' 18 | 4. Thấy lỗi 19 | 20 | **Hành vi mong đợi** 21 | Mô tả rõ ràng và ngắn gọn về điều bạn mong đợi sẽ xảy ra. 22 | 23 | **Ảnh chụp màn hình** 24 | Nếu có, hãy thêm ảnh chụp màn hình để minh họa lỗi. 25 | 26 | **Ngữ cảnh bổ sung** 27 | Thêm bất kỳ thông tin bổ sung nào về sự cố ở đây (thiết bị, trình duyệt, môi trường...). 28 | 29 | **Giải pháp khả thi** 30 | Nếu bạn có gợi ý về cách khắc phục lỗi, vui lòng mô tả tại đây. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE] ' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | 22 | **Target topics/areas** 23 | Which topics or sections of the repository would be affected by this feature? -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Yêu cầu tính năng 3 | about: Đề xuất một ý tưởng hoặc tính năng cho dự án 4 | title: '[FEATURE] ' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Yêu cầu tính năng này có liên quan đến vấn đề nào không? Hãy mô tả.** 11 | Mô tả rõ ràng và ngắn gọn về vấn đề. Ví dụ: Tôi thường cảm thấy khó chịu khi [...] 12 | 13 | **Mô tả giải pháp bạn muốn có** 14 | Mô tả rõ ràng và ngắn gọn về điều bạn muốn xảy ra. 15 | 16 | **Mô tả các giải pháp thay thế bạn đã xem xét** 17 | Mô tả rõ ràng và ngắn gọn về bất kỳ giải pháp thay thế hoặc tính năng khác mà bạn đã cân nhắc. 18 | 19 | **Ngữ cảnh bổ sung** 20 | Thêm bất kỳ thông tin, bối cảnh hoặc ảnh chụp màn hình nào liên quan đến yêu cầu tính năng ở đây. 21 | 22 | **Các chủ đề/khu vực bị ảnh hưởng** 23 | Những chủ đề hoặc phần nào trong repository sẽ bị ảnh hưởng bởi tính năng này? 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | ## Related Issue 5 | 6 | Fixes # (issue) 7 | 8 | ## Type of Change 9 | 10 | - [ ] New content (notes, snippets) 11 | - [ ] Content improvement (updates, fixes, expansion) 12 | - [ ] Documentation update 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] Infrastructure/tooling (CI, scripts, etc.) 15 | - [ ] Translation (i18n) - specify language: ______ 16 | - [ ] Other (please describe): 17 | 18 | ## Checklist 19 | 20 | - [ ] My content follows the style guidelines of this project 21 | - [ ] I have performed a self-review of my own content/code 22 | - [ ] I have included references/links where appropriate 23 | - [ ] My changes generate no new warnings or errors 24 | - [ ] I have checked formatting with markdownlint or similar tools 25 | - [ ] I have checked that all links are valid 26 | - [ ] For translations: I've followed the i18n folder structure (i18n/[language_code]/...) 27 | 28 | ## Additional Notes 29 | 30 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | #github: tanthanhdev 4 | ko_fi: devphan 5 | custom: ["https://www.paypal.me/ThanhPhan481"] 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Markdown CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | check-links: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Setup Node.js 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: '16' 19 | 20 | - name: Install dependencies 21 | run: npm install -g markdown-link-check 22 | 23 | - name: Check links 24 | run: ./tools/check_links.sh 25 | 26 | - name: Generate TOC 27 | run: | 28 | python3 ./tools/generate_summary.py 29 | git diff --exit-code SUMMARY.md || (echo "SUMMARY.md is out of date. Please run ./tools/generate_summary.py and commit the changes" && exit 1) -------------------------------------------------------------------------------- /.github/workflows/update-frontmatter.yml: -------------------------------------------------------------------------------- 1 | name: Update Markdown Frontmatter 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'docs/**/*.md' 7 | - 'i18n/**/*.md' 8 | pull_request: 9 | paths: 10 | - 'docs/**/*.md' 11 | - 'i18n/**/*.md' 12 | schedule: 13 | # Run at 00:00 UTC every Monday 14 | - cron: '0 0 * * 1' 15 | # Allow manual runs from GitHub interface 16 | workflow_dispatch: 17 | 18 | jobs: 19 | update-frontmatter: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v3 24 | with: 25 | # Fetch full history to allow creating commits 26 | fetch-depth: 0 27 | 28 | - name: Setup Node.js 29 | uses: actions/setup-node@v3 30 | with: 31 | node-version: '18' 32 | cache: 'npm' 33 | 34 | - name: Install dependencies 35 | run: npm install gray-matter glob 36 | 37 | - name: Run update script 38 | run: | 39 | chmod +x tools/update-frontmatter.js 40 | node tools/update-frontmatter.js 41 | 42 | - name: Check for changes 43 | id: git-check 44 | run: | 45 | git diff --exit-code || echo "changes=true" >> $GITHUB_OUTPUT 46 | 47 | - name: Commit changes if any 48 | if: steps.git-check.outputs.changes == 'true' 49 | run: | 50 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 51 | git config --local user.name "github-actions[bot]" 52 | git add -A 53 | git commit -m "chore: update markdown frontmatter [skip ci]" 54 | 55 | - name: Push changes 56 | if: steps.git-check.outputs.changes == 'true' 57 | uses: ad-m/github-push-action@master 58 | with: 59 | github_token: ${{ secrets.GITHUB_TOKEN }} 60 | branch: ${{ github.ref }} 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules (if using any JS tools) 2 | node_modules/ 3 | 4 | # Python 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | *.so 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # C# 26 | bin/ 27 | obj/ 28 | *.user 29 | *.suo 30 | *.userprefs 31 | *.sln.docstates 32 | .vs/ 33 | 34 | # VSCode settings 35 | .vscode/ 36 | 37 | # macOS system files 38 | .DS_Store 39 | 40 | # Windows system files 41 | Thumbs.db 42 | ehthumbs.db 43 | Desktop.ini 44 | 45 | # Temporary files 46 | *.log 47 | *.tmp 48 | *.swp 49 | *.swo 50 | *.bak 51 | 52 | # Build output 53 | dist/ 54 | build/ 55 | 56 | # Jupyter notebooks checkpoints (if any) 57 | .ipynb_checkpoints/ 58 | 59 | # Coverage reports 60 | htmlcov/ 61 | coverage.xml 62 | *.cover 63 | 64 | # Ignore generated files and backups 65 | *.orig 66 | *.rej 67 | 68 | # Ignore public site build if using static site generator (optional) 69 | # public/ 70 | .cache/ 71 | 72 | # Ignore Markdown preview temp files (e.g., Typora) 73 | *.assets/ 74 | 75 | # Ignore personal notes or drafts 76 | private-notes/ 77 | drafts/ 78 | 79 | # Docker 80 | .docker-env 81 | .docker-cache 82 | docker-compose.override.yml 83 | 84 | # Environment variables 85 | .env.local 86 | .env.* 87 | .env 88 | !.env.example 89 | docker/environments/databases/.env 90 | 91 | # Not follow website 92 | !website/ -------------------------------------------------------------------------------- /.next/trace: -------------------------------------------------------------------------------- 1 | [{"name":"next-dev","duration":336397,"timestamp":257582122435,"id":1,"tags":{},"startTime":1749254546436,"traceId":"a69c61c90c63e8b2"}] 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender characteristics, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [CONTACT EMAIL]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 44 | 45 | ## Summary 46 | 47 | * Treat everyone with respect and courtesy 48 | * Gracefully accept constructive feedback 49 | * Focus on helping others and growing the community 50 | * Don't use offensive language or imagery 51 | * Don't harass, threaten, or discriminate against anyone 52 | * If you witness inappropriate behavior, report it to project maintainers 53 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT_vi.md: -------------------------------------------------------------------------------- 1 | # Quy tắc Ứng xử của Tech Notes Hub 2 | 3 | ## Cam kết của chúng tôi 4 | 5 | Nhằm thúc đẩy một môi trường mở và thân thiện, chúng tôi - những người đóng góp và duy trì dự án - cam kết đảm bảo việc tham gia vào dự án và cộng đồng của chúng tôi là một trải nghiệm không quấy rối cho tất cả mọi người, bất kể tuổi tác, kích thước cơ thể, khuyết tật, dân tộc, đặc điểm giới tính, bản dạng và biểu hiện giới tính, trình độ kinh nghiệm, quốc tịch, ngoại hình cá nhân, chủng tộc, tôn giáo, hoặc bản dạng và khuynh hướng tình dục. 6 | 7 | ## Tiêu chuẩn của chúng tôi 8 | 9 | Các ví dụ về hành vi góp phần tạo ra môi trường tích cực bao gồm: 10 | 11 | * Sử dụng ngôn ngữ chào đón và hòa nhập 12 | * Tôn trọng các quan điểm và kinh nghiệm khác nhau 13 | * Nhã nhặn chấp nhận phê bình mang tính xây dựng 14 | * Tập trung vào điều tốt nhất cho cộng đồng 15 | * Thể hiện sự đồng cảm với các thành viên khác trong cộng đồng 16 | 17 | Các ví dụ về hành vi không thể chấp nhận từ người tham gia bao gồm: 18 | 19 | * Việc sử dụng ngôn ngữ hoặc hình ảnh mang tính gợi dục và sự chú ý hoặc tiến triển tình dục không được chào đón 20 | * Trolling, bình luận xúc phạm/miệt thị, và tấn công cá nhân hoặc mang tính chính trị 21 | * Quấy rối công khai hoặc riêng tư 22 | * Công bố thông tin cá nhân của người khác, chẳng hạn như địa chỉ vật lý hoặc điện tử, mà không được phép rõ ràng 23 | * Các hành vi khác có thể hợp lý được coi là không phù hợp trong môi trường chuyên nghiệp 24 | 25 | ## Trách nhiệm của chúng tôi 26 | 27 | Người duy trì dự án có trách nhiệm làm rõ các tiêu chuẩn về hành vi có thể chấp nhận được và được mong đợi sẽ thực hiện các hành động khắc phục phù hợp và công bằng để đáp lại bất kỳ trường hợp hành vi không thể chấp nhận nào. 28 | 29 | Người duy trì dự án có quyền và trách nhiệm xóa, chỉnh sửa hoặc từ chối nhận xét, commit, mã, chỉnh sửa wiki, issues và các đóng góp khác không phù hợp với Quy tắc Ứng xử này, hoặc tạm thời hoặc vĩnh viễn cấm bất kỳ người đóng góp nào vì các hành vi khác mà họ cho là không phù hợp, đe dọa, xúc phạm hoặc có hại. 30 | 31 | ## Phạm vi 32 | 33 | Quy tắc Ứng xử này áp dụng trong cả không gian dự án và không gian công cộng khi một cá nhân đại diện cho dự án hoặc cộng đồng của dự án. Ví dụ về việc đại diện cho một dự án hoặc cộng đồng bao gồm việc sử dụng địa chỉ email chính thức của dự án, đăng thông qua tài khoản mạng xã hội chính thức, hoặc làm đại diện được chỉ định tại một sự kiện trực tuyến hoặc ngoại tuyến. Đại diện của một dự án có thể được người duy trì dự án xác định và làm rõ thêm. 34 | 35 | ## Thực thi 36 | 37 | Các trường hợp hành vi lạm dụng, quấy rối hoặc không thể chấp nhận khác có thể được báo cáo bằng cách liên hệ với nhóm dự án tại [EMAIL LIÊN HỆ]. Tất cả khiếu nại sẽ được xem xét và điều tra, và sẽ dẫn đến phản hồi được coi là cần thiết và phù hợp với hoàn cảnh. Nhóm dự án có nghĩa vụ duy trì tính bảo mật đối với người báo cáo về một sự cố. Các chi tiết cụ thể hơn về chính sách thực thi có thể được đăng riêng. 38 | 39 | Người duy trì dự án không tuân theo hoặc thực thi Quy tắc Ứng xử một cách thiện chí có thể phải đối mặt với hậu quả tạm thời hoặc vĩnh viễn do các thành viên lãnh đạo khác của dự án quyết định. 40 | 41 | ## Ghi nhận 42 | 43 | Quy tắc Ứng xử này được điều chỉnh từ [Contributor Covenant](https://www.contributor-covenant.org), phiên bản 1.4, có tại https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 44 | 45 | ## Tóm tắt 46 | 47 | * Đối xử với mọi người bằng sự tôn trọng và lịch sự 48 | * Chấp nhận các góp ý mang tính xây dựng một cách nhã nhặn 49 | * Tập trung vào việc giúp đỡ người khác và phát triển cộng đồng 50 | * Không sử dụng ngôn ngữ hoặc hình ảnh xúc phạm 51 | * Không quấy rối, đe dọa hoặc phân biệt đối xử với bất kỳ ai 52 | * Nếu bạn chứng kiến hành vi không phù hợp, hãy báo cáo cho người quản lý dự án 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Tech Notes Hub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build run-snippet help db-start db-stop db-run-snippet 2 | 3 | help: 4 | @echo "Tech Notes Hub Docker Environment" 5 | @echo "=================================" 6 | @echo "" 7 | @echo "Usage:" 8 | @echo " make build Build all Docker environments" 9 | @echo " make run-snippet FILE=path Run a specific code snippet" 10 | @echo " make db-start DB=type Start a specific database (mysql, postgres, mongodb, redis, sqlite)" 11 | @echo " make db-stop DB=type Stop a specific database" 12 | @echo " make db-run-snippet DB=type FILE=path Run a code snippet with database connection" 13 | @echo " make help Show this help message" 14 | 15 | build: 16 | docker-compose build 17 | 18 | run-snippet: 19 | @if [ -z "$(FILE)" ]; then \ 20 | echo "Error: Please specify a file path. Example: make run-snippet FILE=snippets/algorithms/graph-traversal/graph_traversal.py"; \ 21 | exit 1; \ 22 | fi 23 | ./docker/run-snippet.sh $(FILE) 24 | 25 | db-start: 26 | @if [ -z "$(DB)" ]; then \ 27 | echo "Error: Please specify a database type. Example: make db-start DB=mysql"; \ 28 | exit 1; \ 29 | fi 30 | docker-compose -f docker/environments/databases/docker-compose.yml up -d $(DB) 31 | 32 | db-stop: 33 | @if [ -z "$(DB)" ]; then \ 34 | echo "Error: Please specify a database type. Example: make db-stop DB=mysql"; \ 35 | exit 1; \ 36 | fi 37 | docker-compose -f docker/environments/databases/docker-compose.yml stop $(DB) 38 | 39 | db-run-snippet: 40 | @if [ -z "$(DB)" ]; then \ 41 | echo "Error: Please specify a database type. Example: make db-run-snippet DB=mysql FILE=path/to/file.py"; \ 42 | exit 1; \ 43 | fi 44 | @if [ -z "$(FILE)" ]; then \ 45 | echo "Error: Please specify a file path. Example: make db-run-snippet DB=mysql FILE=path/to/file.py"; \ 46 | exit 1; \ 47 | fi 48 | ./docker/run-db-snippet.sh $(DB) $(FILE) 49 | -------------------------------------------------------------------------------- /PULL_REQUEST_RULES.md: -------------------------------------------------------------------------------- 1 | # 📏 Pull Request Rules for Tech Notes Hub 2 | 3 | To maintain the quality, clarity, and consistency of this knowledge base, we have a few rules for what is allowed (✅) and not allowed (❌) in pull requests. 4 | 5 | Please read carefully before submitting a PR. 6 | 7 | --- 8 | 9 | ## 📝 Pull Request Template 10 | 11 | When submitting a pull request, please include: 12 | 13 | ### Description 14 | A clear description of the changes you have made. 15 | 16 | ### Related Issue 17 | Link to any related issues: `Fixes #(issue number)` 18 | 19 | ### Type of Change 20 | Select the appropriate options: 21 | - [ ] New content (notes, snippets) 22 | - [ ] Content improvement (updates, fixes, expansion) 23 | - [ ] Documentation update 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] Infrastructure/tooling (CI, scripts, etc.) 26 | - [ ] Translation (i18n) - specify language: ______ 27 | - [ ] Other (please describe) 28 | 29 | ### Checklist 30 | - [ ] My content follows the style guidelines of this project 31 | - [ ] I have performed a self-review of my own content/code 32 | - [ ] I have included references/links where appropriate 33 | - [ ] My changes generate no new warnings or errors 34 | - [ ] I have checked that all links are valid 35 | - [ ] For translations: I've followed the i18n folder structure (i18n/[language_code]/...) 36 | 37 | --- 38 | 39 | ## ✅ Allowed in Pull Requests 40 | 41 | - **New content** such as: 42 | - Well-written technical notes 43 | - New algorithms, patterns, or architecture topics 44 | - AWS/cloud service notes 45 | - Interview-prep or reference guides 46 | - **Code snippets** that: 47 | - Are correct, clear, and minimal 48 | - Follow syntax best practices 49 | - Include inline comments if helpful 50 | - **Improvements** to: 51 | - Existing explanations or formatting 52 | - Typo and grammar fixes 53 | - Markdown formatting or link corrections 54 | - **New language versions**: 55 | - Translations in the `i18n/[language_code]/` folder structure (e.g., `i18n/vi/`, `i18n/fr/`) 56 | - Must match the structure and logic of the original file 57 | - Should be referenced in the SUMMARY.md under appropriate language section 58 | 59 | ## ❌ Not Allowed in Pull Requests 60 | 61 | - ❌ **AI-generated content** without human review or editing 62 | - ❌ Low-effort or duplicate content (e.g., copy-paste from blogs) 63 | - ❌ Off-topic notes not related to software engineering or dev practices 64 | - ❌ Large dumps of code without explanation or context 65 | - ❌ Personal promotion, affiliate links, or ads 66 | - ❌ Files with broken structure, invalid markdown, or irrelevant naming 67 | - ❌ Notes that contain **plagiarized content** (copying from copyrighted materials) 68 | - ❌ Translations not following the proper i18n folder structure 69 | 70 | ## 🔖 Style & Structure Reminders 71 | 72 | - Use clear, **conversational but concise** explanations 73 | - Use correct heading hierarchy (`#`, `##`, `###`, etc.) 74 | - Name files using lowercase and underscore: `binary_search.md` 75 | - Place content in the appropriate folder (`algorithms/`, `design-patterns/`, etc.) 76 | - For translations, use the `i18n/[language_code]/` structure, mirroring the main docs structure 77 | - Update SUMMARY.md to include new content or translations 78 | 79 | ## 📢 Final Note 80 | 81 | We appreciate your contribution and effort! 82 | All pull requests will be reviewed by maintainers before merging. 83 | Feel free to open a discussion issue if you're unsure whether something fits. 84 | 85 | Let's build a high-quality, developer-friendly knowledge base together. 🚀 86 | -------------------------------------------------------------------------------- /PULL_REQUEST_RULES_vi.md: -------------------------------------------------------------------------------- 1 | # 📏 Quy định Pull Request cho Tech Notes Hub 2 | 3 | Để đảm bảo chất lượng, tính nhất quán và rõ ràng của kho kiến thức này, chúng tôi đặt ra một số quy định về **những gì được phép (✅) và không được phép (❌)** khi tạo pull request. 4 | 5 | Vui lòng đọc kỹ trước khi gửi PR. 6 | 7 | --- 8 | 9 | ## 📝 Mẫu Pull Request 10 | 11 | Khi gửi pull request, vui lòng bao gồm: 12 | 13 | ### Mô tả 14 | Mô tả rõ ràng về các thay đổi bạn đã thực hiện. 15 | 16 | ### Vấn đề liên quan 17 | Liên kết đến các vấn đề liên quan: `Fixes #(số issue)` 18 | 19 | ### Loại thay đổi 20 | Chọn các tùy chọn thích hợp: 21 | - [ ] Nội dung mới (ghi chú, đoạn mã) 22 | - [ ] Cải thiện nội dung (cập nhật, sửa lỗi, mở rộng) 23 | - [ ] Cập nhật tài liệu 24 | - [ ] Sửa lỗi (thay đổi không gây ảnh hưởng) 25 | - [ ] Cơ sở hạ tầng/công cụ (CI, scripts, v.v.) 26 | - [ ] Bản dịch (i18n) - chỉ rõ ngôn ngữ: ______ 27 | - [ ] Khác (vui lòng mô tả) 28 | 29 | ### Danh sách kiểm tra 30 | - [ ] Nội dung của tôi tuân theo hướng dẫn về phong cách của dự án 31 | - [ ] Tôi đã tự đánh giá nội dung/mã của mình 32 | - [ ] Tôi đã bao gồm tài liệu tham khảo/liên kết khi thích hợp 33 | - [ ] Các thay đổi của tôi không tạo ra cảnh báo hoặc lỗi mới 34 | - [ ] Tôi đã kiểm tra rằng tất cả các liên kết đều hợp lệ 35 | - [ ] Đối với bản dịch: Tôi đã tuân theo cấu trúc thư mục i18n (i18n/[language_code]/...) 36 | 37 | --- 38 | 39 | ## ✅ Được phép trong Pull Request 40 | 41 | - **Thêm nội dung mới** như: 42 | - Ghi chú kỹ thuật được trình bày rõ ràng 43 | - Các thuật toán, mẫu thiết kế, kiến trúc phần mềm 44 | - Ghi chú về dịch vụ AWS hoặc điện toán đám mây 45 | - Tài liệu ôn tập phỏng vấn hoặc tra cứu nhanh 46 | - **Đoạn mã ví dụ**: 47 | - Đúng cú pháp, dễ hiểu, tối giản 48 | - Theo chuẩn coding style 49 | - Có chú thích nếu cần 50 | - **Cải thiện nội dung**: 51 | - Giải thích rõ hơn, định dạng dễ đọc hơn 52 | - Sửa lỗi chính tả, ngữ pháp 53 | - Cập nhật liên kết hoặc định dạng Markdown 54 | - **Bản dịch ghi chú**: 55 | - Sử dụng cấu trúc thư mục `i18n/[mã_ngôn_ngữ]/` (ví dụ: `i18n/vi/`, `i18n/es/`) 56 | - Nội dung bám sát logic file gốc 57 | - Cập nhật trong SUMMARY.md dưới phần ngôn ngữ tương ứng 58 | 59 | ## ❌ Không được phép trong Pull Request 60 | 61 | - ❌ **Nội dung tạo bởi AI** mà không qua chỉnh sửa thủ công hoặc kiểm duyệt 62 | - ❌ Nội dung sơ sài, trùng lặp, sao chép từ blog/diễn đàn 63 | - ❌ Ghi chú không liên quan đến lập trình hoặc kỹ thuật phần mềm 64 | - ❌ Dump code dài dòng, không giải thích, không rõ mục đích 65 | - ❌ Chèn liên kết quảng cáo, giới thiệu cá nhân, affiliate link 66 | - ❌ File đặt tên sai quy tắc, không dùng định dạng Markdown hợp lệ 67 | - ❌ Nội dung **sao chép có bản quyền** từ nguồn khác 68 | - ❌ Bản dịch không tuân thủ cấu trúc thư mục i18n đúng quy định 69 | 70 | ## 🔖 Lưu ý về cấu trúc & định dạng 71 | 72 | - Trình bày **ngắn gọn, dễ hiểu, gần gũi** 73 | - Sử dụng đúng cấp độ tiêu đề: `#`, `##`, `###`,... 74 | - Đặt tên file bằng chữ thường, dùng dấu gạch dưới: `binary_search.md` 75 | - Đặt đúng thư mục docs (`docs/algorithms/`, `docs/design-patterns/`, `docs/aws/`,...) 76 | - Đối với bản dịch, sử dụng cấu trúc `i18n/[mã_ngôn_ngữ]/`, phản ánh cấu trúc thư mục chính 77 | - Cập nhật SUMMARY.md để bao gồm nội dung mới hoặc bản dịch 78 | 79 | ## 📢 Lưu ý cuối 80 | 81 | Cảm ơn bạn đã đóng góp cho dự án! 82 | Tất cả pull request sẽ được review kỹ trước khi merge. 83 | Nếu chưa chắc nội dung có phù hợp không, bạn có thể mở một issue để trao đổi trước. 84 | 85 | Cùng nhau xây dựng một kho kiến thức chất lượng cho lập trình viên! 🚀 86 | 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub – Technical notes & practical code snippets 2 | 3 | **All-in-one technical notes & code snippets** — A centralized knowledge base covering design patterns, algorithms, data structures, AWS, and more. Perfect for learning, quick reference, and daily developer use. 4 | 5 | 📄 This README is also available in other languages: 6 | 7 | - 🇻🇳 [Tiếng Việt](README_vi.md) 8 | 9 | --- 10 | 11 | ## 📚 Overview 12 | 13 | Tech Notes Hub is a curated collection of technical notes and code snippets aimed at helping developers deepen their understanding and speed up coding tasks. This repository covers a wide range of topics including: 14 | 15 | * Design Patterns 16 | * Algorithms & Data Structures 17 | * Cloud & AWS Services 18 | * Software Architecture 19 | * System Design 20 | * Best Practices & Tips 21 | 22 | and many more. 23 | 24 | It's designed to be your go-to resource whether you're preparing for interviews, building projects, or learning new concepts. 25 | 26 | ## 🚀 Features 27 | 28 | * Well-organized notes and examples 29 | * Clear and concise explanations 30 | * Language-agnostic concepts with code snippets in popular languages 31 | * Regularly updated content 32 | * Easy to browse and search 33 | 34 | ## 📂 Contents 35 | 36 | * **Design Patterns:** Singleton, Factory, Observer, Strategy, etc. 37 | * **Algorithms:** Sorting, Searching, Graphs, Dynamic Programming 38 | * **Data Structures:** Arrays, Linked Lists, Trees, Graphs, Hash Tables 39 | * **AWS:** EC2, S3, Lambda, CloudFormation, IAM, and more 40 | * **System Design:** Scalability, Caching, Load Balancing 41 | * **Miscellaneous:** DevOps, CI/CD, Security tips, etc. 42 | 43 | ## 💡 Why Use Tech Notes Hub? 44 | 45 | * Centralized knowledge saves time searching multiple resources 46 | * Clear code snippets make concepts easy to understand and apply 47 | * Great for interview prep and daily coding challenges 48 | * Open source and community-driven — contributions welcome! 49 | 50 | ## 📖 How to Use 51 | 52 | Simply browse the folders or use GitHub's search feature to find the topic or pattern you need. Each note is designed to be self-contained with theory and practical code. 53 | 54 | **For a complete table of contents with all available notes and resources, check out the [SUMMARY.md](SUMMARY.md) file.** 55 | 56 | ### 🐳 Docker Environments 57 | 58 | This repository includes Docker configurations for running code snippets in various programming languages. To run a code snippet: 59 | 60 | ```bash 61 | ./docker/run-snippet.sh snippets/path/to/your/snippet.py 62 | ``` 63 | 64 | For more information on Docker usage, see the [Docker README](docker/README.md). 65 | 66 | ### 🛠️ Utilities 67 | 68 | This repository contains utility scripts in the `/tools` directory to help maintain the documentation. 69 | 70 | #### Updating Markdown Frontmatter 71 | 72 | The `update-frontmatter.js` script automatically updates metadata in Markdown files: 73 | 74 | ```bash 75 | # First, install Node.js dependencies 76 | npm install 77 | 78 | # Run the script (updates all Markdown files) 79 | node tools/update-frontmatter.js 80 | 81 | # Update a specific file 82 | node tools/update-frontmatter.js algorithms/sorting-algorithms.md 83 | ``` 84 | 85 | #### Generating Table of Contents 86 | 87 | The `generate_summary.py` script automatically creates the SUMMARY.md file: 88 | 89 | ```bash 90 | # Run the script to generate a new table of contents 91 | python tools/generate_summary.py 92 | ``` 93 | 94 | For more information about available tools, see the [Tools README](tools/README.md). 95 | 96 | ## 🤝 Contribution 97 | 98 | Contributions are highly welcome! If you want to: 99 | 100 | * Add new notes or code snippets 101 | * Improve existing explanations or examples 102 | * Report issues or suggest new topics 103 | 104 | Please follow the guidelines in the [CONTRIBUTING.md](CONTRIBUTING.md) file. 105 | 106 | Before submitting a pull request, make sure to check the [Pull Request Rules](PULL_REQUEST_RULES.md) to see what's allowed. ✅ 107 | 108 | ## 📜 License 109 | 110 | This project is licensed under the MIT License. See the [LICENSE](LICENSE.txt) file for details. 111 | 112 | ## 📝 Changelog 113 | 114 | For a detailed list of all notable changes to this project, please see the [changelog](changelog.md) file. 115 | 116 | ## 🙌 Acknowledgements 117 | 118 | Thanks to all contributors and the open source community for making this knowledge base better every day. 119 | 120 | ## 📬 Contact 121 | 122 | If you have any questions or suggestions, feel free to open an issue or contact the maintainer: 123 | 124 | * GitHub: [tanthanhdev](https://github.com/tanthanhdev) 125 | 126 | --- 127 | 128 | **Happy coding!** 🚀 129 | -------------------------------------------------------------------------------- /README_WEBSITE.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub Blog Website 2 | 3 | This is the blog website component of the Tech Notes Hub project. The website is built using Next.js, Tailwind CSS, shadcn UI, and i18n support for multilingual content (Vietnamese and English). 4 | 5 | ## Overview 6 | 7 | The blog website serves as a frontend for displaying technical content and notes from the Tech Notes Hub repository. It provides a modern, responsive interface for users to browse and read technical articles. 8 | 9 | ## Getting Started 10 | 11 | The blog website is located in the `/website` directory. To start working with it: 12 | 13 | ```bash 14 | cd website 15 | npm install 16 | npm run dev 17 | ``` 18 | 19 | Open [http://localhost:3000](http://localhost:3000) to view the site in your browser. 20 | 21 | ## Features 22 | 23 | - **Modern UI**: Clean, minimal design with Tailwind CSS and shadcn UI components 24 | - **Bilingual Support**: Full i18n integration with Vietnamese and English translations 25 | - **Blog System**: Complete blog functionality with Markdown content rendering 26 | - **Dark/Light Mode**: Theme switching with system preference detection 27 | - **SEO Optimized**: Meta tags and OpenGraph support for better search engine visibility 28 | - **Responsive Design**: Mobile-first approach, works on all devices 29 | 30 | ## Tech Stack 31 | 32 | - [Next.js 14](https://nextjs.org/) - React framework 33 | - [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework 34 | - [shadcn UI](https://ui.shadcn.com/) - UI component library 35 | - [React Markdown](https://github.com/remarkjs/react-markdown) - Markdown renderer 36 | - [i18n](https://nextjs.org/docs/app/building-your-application/routing/internationalization) - Internationalization 37 | - [next-themes](https://github.com/pacocoursey/next-themes) - Theme management 38 | 39 | ## Documentation 40 | 41 | For detailed information about the blog website, including setup instructions, project structure, and deployment guidelines, please refer to the [Website README](/website/README.md) in the website directory. 42 | -------------------------------------------------------------------------------- /README_vi.md: -------------------------------------------------------------------------------- 1 | # Tech Notes - Technical notes & practical code snippets 2 | 3 | **Ghi chú kỹ thuật & mã nguồn tổng hợp** — Một kho kiến thức tập trung, bao gồm Design Patterns, Thuật toán, Cấu trúc dữ liệu, AWS và nhiều chủ đề khác. Hoàn hảo cho việc học tập, tra cứu nhanh, và sử dụng hàng ngày của lập trình viên. 4 | 5 | 📄 Bạn cũng có thể xem README này bằng tiếng Anh: 6 | 7 | - 🇺🇸 [English](README.md) 8 | 9 | --- 10 | 11 | ## 📚 Tổng quan 12 | 13 | **Tech Notes Hub** là bộ sưu tập được chọn lọc kỹ lưỡng gồm các ghi chú kỹ thuật và đoạn mã nhằm giúp lập trình viên hiểu sâu hơn và tăng tốc khi làm việc. Kho tài liệu này bao gồm nhiều chủ đề như: 14 | 15 | * Design Patterns (Mẫu thiết kế) 16 | * Thuật toán & Cấu trúc dữ liệu 17 | * Điện toán đám mây & Dịch vụ AWS 18 | * Kiến trúc phần mềm 19 | * Thiết kế hệ thống 20 | * Thực tiễn tốt & mẹo lập trình 21 | 22 | ...và nhiều chủ đề khác. 23 | 24 | Dự án được thiết kế để trở thành tài nguyên tham khảo hàng đầu của bạn — dù là khi học, làm dự án, hay chuẩn bị phỏng vấn. 25 | 26 | ## 🚀 Tính năng 27 | 28 | * Ghi chú rõ ràng, có cấu trúc khoa học 29 | * Giải thích ngắn gọn, dễ hiểu 30 | * Ý tưởng không phụ thuộc ngôn ngữ, có kèm mã ví dụ bằng các ngôn ngữ phổ biến 31 | * Nội dung được cập nhật định kỳ 32 | * Dễ dàng tra cứu và tìm kiếm 33 | 34 | ## 📂 Nội dung chính 35 | 36 | * **Design Patterns:** Singleton, Factory, Observer, Strategy,... 37 | * **Thuật toán:** Sắp xếp, Tìm kiếm, Đồ thị, Quy hoạch động 38 | * **Cấu trúc dữ liệu:** Mảng, Danh sách liên kết, Cây, Đồ thị, Bảng băm 39 | * **AWS:** EC2, S3, Lambda, CloudFormation, IAM,... 40 | * **Thiết kế hệ thống:** Khả năng mở rộng, Bộ nhớ đệm, Cân bằng tải 41 | * **Khác:** DevOps, CI/CD, Bảo mật, v.v. 42 | 43 | ## 💡 Vì sao nên dùng Tech Notes Hub? 44 | 45 | * Tiết kiệm thời gian nhờ kiến thức tập trung, dễ tra cứu 46 | * Đoạn mã rõ ràng giúp dễ hiểu và áp dụng 47 | * Phù hợp với ôn luyện phỏng vấn hoặc giải bài tập hàng ngày 48 | * Là dự án mã nguồn mở, được cộng đồng đóng góp liên tục 49 | 50 | ## 📖 Cách sử dụng 51 | 52 | Bạn có thể duyệt các thư mục hoặc dùng tính năng tìm kiếm của GitHub để tra cứu chủ đề hoặc mẫu thiết kế bạn cần. 53 | Mỗi ghi chú đều độc lập, bao gồm lý thuyết và mã ví dụ thực tế. 54 | 55 | **Để xem danh mục đầy đủ với tất cả các ghi chú và tài nguyên có sẵn, hãy xem file [SUMMARY.md](SUMMARY.md).** 56 | 57 | ### 🐳 Môi Trường Docker 58 | 59 | Kho lưu trữ này bao gồm cấu hình Docker để chạy đoạn mã trong nhiều ngôn ngữ lập trình khác nhau. Để chạy một đoạn mã: 60 | 61 | ```bash 62 | ./docker/run-snippet.sh snippets/path/to/your/snippet.py 63 | ``` 64 | 65 | Để biết thêm thông tin về cách sử dụng Docker, hãy xem [Docker README](docker/README_vi.md). 66 | 67 | ### 🛠️ Công cụ tiện ích 68 | 69 | Kho lưu trữ này chứa các script tiện ích trong thư mục `/tools` để hỗ trợ việc duy trì tài liệu. 70 | 71 | #### Cập nhật Frontmatter cho file Markdown 72 | 73 | Script `update-frontmatter.js` tự động cập nhật metadata trong các file Markdown: 74 | 75 | ```bash 76 | # Đầu tiên, cài đặt các gói phụ thuộc Node.js 77 | npm install 78 | 79 | # Chạy script (cập nhật tất cả file Markdown) 80 | node tools/update-frontmatter.js 81 | 82 | # Cập nhật một file cụ thể 83 | node tools/update-frontmatter.js algorithms/sorting-algorithms.md 84 | ``` 85 | 86 | #### Tạo mục lục tự động 87 | 88 | Script `generate_summary.py` tự động tạo file SUMMARY.md: 89 | 90 | ```bash 91 | # Chạy script để tạo mục lục mới 92 | python tools/generate_summary.py 93 | ``` 94 | 95 | Để biết thêm thông tin về các công cụ có sẵn, hãy xem [README của Tools](tools/README.md). 96 | 97 | ## 🤝 Đóng góp 98 | 99 | Mọi đóng góp đều rất hoan nghênh! Nếu bạn muốn: 100 | 101 | * Thêm ghi chú hoặc đoạn mã mới 102 | * Cải thiện nội dung hiện tại 103 | * Báo lỗi hoặc đề xuất chủ đề mới 104 | 105 | Hãy xem hướng dẫn trong file [CONTRIBUTING_vi.md](CONTRIBUTING_vi.md). 106 | 107 | Trước khi gửi pull request, vui lòng đọc kỹ [Quy định nội dung Pull Request](PULL_REQUEST_RULES_vi.md) để biết những gì được chấp nhận. ✅ 108 | 109 | ## 📜 Giấy phép 110 | 111 | Dự án này được phát hành theo giấy phép MIT. Xem chi tiết trong file [LICENSE](LICENSE.txt). 112 | 113 | ## 📝 Changelog 114 | 115 | Để biết danh sách chi tiết về tất cả những thay đổi đáng chú ý trong dự án này, vui lòng xem tệp [changelog](changelog_vi.md). 116 | 117 | ## 🙌 Lời cảm ơn 118 | 119 | Cảm ơn tất cả các contributors và cộng đồng mã nguồn mở đã cùng nhau làm phong phú thêm kho kiến thức này mỗi ngày. 120 | 121 | ## 📬 Liên hệ 122 | 123 | Nếu bạn có câu hỏi hoặc góp ý, hãy mở một issue hoặc liên hệ với maintainer: 124 | 125 | * GitHub: [tanthanhdev](https://github.com/tanthanhdev) 126 | 127 | --- 128 | 129 | **Chúc bạn code vui vẻ!** 🚀 130 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub 2 | 3 | ## Table of Contents 4 | 5 | ### Algorithms 6 | 7 | - [Graph Traversal Algorithms](docs/algorithms/graph-traversal.md) 8 | - [Sorting Algorithms](docs/algorithms/sorting-algorithms.md) 9 | 10 | ### Databases 11 | 12 | - [Relational Databases](docs/databases/relational.md) 13 | 14 | ### Design Patterns 15 | 16 | - [Factory Design Pattern](docs/design-patterns/factory.md) 17 | - [Observer Design Pattern](docs/design-patterns/observer.md) 18 | - [Singleton Design Pattern](docs/design-patterns/singleton.md) 19 | 20 | ### Devops 21 | 22 | - [Continuous Integration and Continuous Deployment (CI/CD)](docs/devops/ci-cd.md) 23 | 24 | ### Linux 25 | 26 | - [Bash Scripting](docs/linux/bash-scripting.md) 27 | 28 | ### System Design 29 | 30 | - [Microservices Architecture](docs/system-design/microservices.md) 31 | 32 | ### Testing 33 | 34 | - [Unit Testing](docs/testing/unit-testing.md) 35 | 36 | -------------------------------------------------------------------------------- /assets/diagrams/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/assets/diagrams/init.txt -------------------------------------------------------------------------------- /assets/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/assets/init.txt -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Big changelog 2 | 3 | All notable changes to the Tech Notes Hub project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | - Security enhancement: Moved database credentials to environment variables 12 | - Added database code examples for Python and C# 13 | - Added design pattern implementations (Observer, Factory, Singleton) 14 | - Created comprehensive documentation in English and Vietnamese 15 | 16 | ### Changed 17 | - Restructured repository to match documentation and code examples 18 | - Updated SQL examples to use parameterized queries for better security 19 | - Improved code organization in snippets directory 20 | 21 | ### Security 22 | - Removed hardcoded database credentials 23 | - Added .env.example file with placeholder values 24 | - Updated .gitignore to prevent committing sensitive information 25 | 26 | ## [1.0.0] - 2025-06-04 27 | 28 | ### Added 29 | - Initial repository structure 30 | - Basic documentation framework 31 | - Core code examples for algorithms and data structures 32 | -------------------------------------------------------------------------------- /changelog_vi.md: -------------------------------------------------------------------------------- 1 | # Nhật ký thay đổi lớn 2 | 3 | Tất cả những thay đổi đáng chú ý đối với dự án Tech Notes Hub sẽ được ghi lại trong tệp này. 4 | 5 | Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | và dự án này tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Chưa phát hành] 9 | 10 | ### Đã thêm 11 | - Tăng cường bảo mật: Chuyển thông tin đăng nhập cơ sở dữ liệu vào biến môi trường 12 | - Thêm các ví dụ mã nguồn cơ sở dữ liệu cho Python và C# 13 | - Thêm các triển khai mẫu thiết kế (Observer, Factory, Singleton) 14 | - Tạo tài liệu toàn diện bằng tiếng Anh và tiếng Việt 15 | 16 | ### Đã thay đổi 17 | - Cơ cấu lại kho lưu trữ để phù hợp với tài liệu và các ví dụ mã nguồn 18 | - Cập nhật các ví dụ SQL để sử dụng truy vấn có tham số để bảo mật tốt hơn 19 | - Cải thiện tổ chức mã trong thư mục snippets 20 | 21 | ### Bảo mật 22 | - Loại bỏ thông tin đăng nhập cơ sở dữ liệu được mã hóa cứng 23 | - Thêm tệp .env.example với các giá trị giữ chỗ 24 | - Cập nhật .gitignore để ngăn chặn việc commit thông tin nhạy cảm 25 | 26 | ## [1.0.0] - 2025-06-04 27 | 28 | ### Đã thêm 29 | - Cấu trúc kho lưu trữ ban đầu 30 | - Khung tài liệu cơ bản 31 | - Các ví dụ mã nguồn cốt lõi cho thuật toán và cấu trúc dữ liệu 32 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | # Python environment 5 | python: 6 | build: 7 | context: . 8 | dockerfile: docker/environments/python/Dockerfile 9 | volumes: 10 | - ./snippets:/app/snippets 11 | working_dir: /app 12 | command: ["--version"] 13 | 14 | # JavaScript/Node.js environment 15 | javascript: 16 | build: 17 | context: . 18 | dockerfile: docker/environments/javascript/Dockerfile 19 | volumes: 20 | - ./snippets:/app/snippets 21 | working_dir: /app 22 | command: ["--version"] 23 | 24 | # Java environment 25 | java: 26 | build: 27 | context: . 28 | dockerfile: docker/environments/java/Dockerfile 29 | volumes: 30 | - ./snippets:/app/snippets 31 | working_dir: /app 32 | command: ["/bin/bash", "-c", "java -version"] 33 | 34 | # C/C++ environment 35 | cpp: 36 | build: 37 | context: . 38 | dockerfile: docker/environments/cpp/Dockerfile 39 | volumes: 40 | - ./snippets:/app/snippets 41 | working_dir: /app 42 | command: ["/bin/bash", "-c", "g++ --version"] 43 | 44 | # Go environment 45 | go: 46 | build: 47 | context: . 48 | dockerfile: docker/environments/go/Dockerfile 49 | volumes: 50 | - ./snippets:/app/snippets 51 | working_dir: /app 52 | command: ["version"] 53 | 54 | # Rust environment 55 | rust: 56 | build: 57 | context: . 58 | dockerfile: docker/environments/rust/Dockerfile 59 | volumes: 60 | - ./snippets:/app/snippets 61 | working_dir: /app 62 | command: ["/bin/bash", "-c", "rustc --version"] 63 | 64 | # PHP environment 65 | php: 66 | build: 67 | context: . 68 | dockerfile: docker/environments/php/Dockerfile 69 | volumes: 70 | - ./snippets:/app/snippets 71 | working_dir: /app 72 | command: ["--version"] 73 | 74 | # C# environment 75 | csharp: 76 | build: 77 | context: . 78 | dockerfile: docker/environments/csharp/Dockerfile 79 | volumes: 80 | - ./snippets:/app/snippets 81 | working_dir: /app 82 | command: ["/bin/bash", "-c", "dotnet --version"] 83 | 84 | # Ruby environment 85 | ruby: 86 | build: 87 | context: . 88 | dockerfile: docker/environments/ruby/Dockerfile 89 | volumes: 90 | - ./snippets:/app/snippets 91 | working_dir: /app 92 | command: ["--version"] 93 | 94 | # Shell environment for Linux and DevOps scripts 95 | shell: 96 | build: 97 | context: . 98 | dockerfile: docker/environments/shell/Dockerfile 99 | volumes: 100 | - ./snippets:/app/snippets 101 | working_dir: /app 102 | command: ["-c", "echo 'Shell environment ready'"] 103 | 104 | # Databricks/PySpark environment 105 | databricks: 106 | build: 107 | context: . 108 | dockerfile: docker/environments/databricks/Dockerfile 109 | volumes: 110 | - ./snippets:/app/snippets 111 | working_dir: /app 112 | command: ["--version"] 113 | -------------------------------------------------------------------------------- /docker/DATABASE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Database Usage Guide with Docker 2 | 3 | This guide helps you run code snippets with connections to databases in Docker environments. 4 | 5 | ## Supported Databases 6 | 7 | - **MySQL** (8.0) 8 | - **PostgreSQL** (15) 9 | - **MongoDB** (6) 10 | - **Redis** (7) 11 | - **SQLite** (3.x) 12 | 13 | ## How to Use 14 | 15 | ### 1. Start a Specific Database 16 | 17 | ```bash 18 | # Using Docker Compose directly 19 | docker-compose -f docker/environments/databases/docker-compose.yml up -d mysql 20 | 21 | # Or using the Makefile 22 | make db-start DB=mysql 23 | ``` 24 | 25 | Valid values for DB are: `mysql`, `postgres`, `mongodb`, `redis`, `sqlite` 26 | 27 | ### 2. Run a Code Snippet with Database Connection 28 | 29 | ```bash 30 | # Using the script directly 31 | ./docker/run-db-snippet.sh mysql snippets/databases/mysql_example.py 32 | 33 | # Or using the Makefile 34 | make db-run-snippet DB=mysql FILE=snippets/databases/mysql_example.py 35 | ``` 36 | 37 | ### 3. Stop a Database When Not in Use 38 | 39 | ```bash 40 | # Using Docker Compose directly 41 | docker-compose -f docker/environments/databases/docker-compose.yml stop mysql 42 | 43 | # Or using the Makefile 44 | make db-stop DB=mysql 45 | ``` 46 | 47 | ## Database Connection Information 48 | 49 | When running a code snippet with a database, the following environment variables will be automatically passed to the container: 50 | 51 | - `DB_HOST`: Database hostname 52 | - `DB_PORT`: Database port 53 | - `DB_USER`: Username for connection 54 | - `DB_PASS`: Password for connection 55 | - `DB_NAME`: Database name 56 | - `DB_CONN_STR`: Full connection string 57 | 58 | ### Default Connection Strings 59 | 60 | - **MySQL**: `mysql://user:password@tech-notes-mysql:3306/tech_notes` 61 | - **PostgreSQL**: `postgresql://user:password@tech-notes-postgres:5432/tech_notes` 62 | - **MongoDB**: `mongodb://user:password@tech-notes-mongodb:27017/tech_notes` 63 | - **Redis**: `redis://tech-notes-redis:6379` 64 | - **SQLite**: `sqlite:///data/tech_notes.db` 65 | 66 | ### Environment Variables Configuration 67 | 68 | To change the default connection information, you can create a `.env` file in the `docker/environments/databases/` directory: 69 | 70 | 1. Copy the `.env.example` file to `.env`: 71 | ```bash 72 | cp docker/environments/databases/.env.example docker/environments/databases/.env 73 | ``` 74 | 75 | 2. Edit the `.env` file to change the connection information (usernames, passwords, database names, etc.) 76 | 77 | 3. When running the `run-db-snippet.sh` command or `make db-run-snippet`, the environment variables from the `.env` file will be automatically used. 78 | 79 | Note: The `.env` file has been added to `.gitignore` to prevent it from being tracked by Git, ensuring sensitive information is not pushed to the repository. 80 | 81 | ## Example Connection Code 82 | 83 | ### Python with MySQL 84 | 85 | ```python 86 | import os 87 | import mysql.connector 88 | 89 | # Get connection info from environment variables 90 | db_host = os.environ.get('DB_HOST', 'localhost') 91 | db_port = os.environ.get('DB_PORT', '3306') 92 | db_user = os.environ.get('DB_USER', 'user') 93 | db_pass = os.environ.get('DB_PASS', 'password') 94 | db_name = os.environ.get('DB_NAME', 'tech_notes') 95 | 96 | # Connect to the database 97 | conn = mysql.connector.connect( 98 | host=db_host, 99 | port=db_port, 100 | user=db_user, 101 | password=db_pass, 102 | database=db_name 103 | ) 104 | 105 | cursor = conn.cursor() 106 | cursor.execute("SELECT * FROM users") 107 | users = cursor.fetchall() 108 | 109 | for user in users: 110 | print(user) 111 | 112 | conn.close() 113 | ``` 114 | 115 | ### JavaScript with MongoDB 116 | 117 | ```javascript 118 | const { MongoClient } = require('mongodb'); 119 | 120 | // Get connection string from environment variable 121 | const uri = process.env.DB_CONN_STR || 'mongodb://user:password@localhost:27017/tech_notes'; 122 | 123 | async function main() { 124 | const client = new MongoClient(uri); 125 | 126 | try { 127 | await client.connect(); 128 | const database = client.db('tech_notes'); 129 | const users = database.collection('users'); 130 | 131 | const query = {}; 132 | const cursor = users.find(query); 133 | 134 | if ((await cursor.count()) === 0) { 135 | console.log("No documents found!"); 136 | } 137 | 138 | await cursor.forEach(user => { 139 | console.log(user); 140 | }); 141 | 142 | } finally { 143 | await client.close(); 144 | } 145 | } 146 | 147 | main().catch(console.error); 148 | ``` 149 | 150 | ## Sample Data 151 | 152 | Each database has been configured with the following sample data: 153 | 154 | - `users` table/collection with 3 users 155 | - `posts` table/collection with 4 posts linked to users 156 | 157 | You can see the details of the sample data in the init files in the respective directory of each database. 158 | 159 | -------------------------------------------------------------------------------- /docker/DATABASE_GUIDE_vi.md: -------------------------------------------------------------------------------- 1 | # Hướng Dẫn Sử Dụng Database với Docker 2 | 3 | Hướng dẫn này giúp bạn chạy code snippets có kết nối tới các database trong môi trường Docker. 4 | 5 | ## Các Database Được Hỗ Trợ 6 | 7 | - **MySQL** (8.0) 8 | - **PostgreSQL** (15) 9 | - **MongoDB** (6) 10 | - **Redis** (7) 11 | - **SQLite** (3.x) 12 | 13 | ## Cách Sử Dụng 14 | 15 | ### 1. Khởi động một Database cụ thể 16 | 17 | ```bash 18 | # Sử dụng Docker Compose trực tiếp 19 | docker-compose -f docker/environments/databases/docker-compose.yml up -d mysql 20 | 21 | # Hoặc sử dụng Makefile 22 | make db-start DB=mysql 23 | ``` 24 | 25 | Các giá trị hợp lệ cho DB là: `mysql`, `postgres`, `mongodb`, `redis`, `sqlite` 26 | 27 | ### 2. Chạy Code Snippet với kết nối Database 28 | 29 | ```bash 30 | # Sử dụng script trực tiếp 31 | ./docker/run-db-snippet.sh mysql snippets/databases/mysql_example.py 32 | 33 | # Hoặc sử dụng Makefile 34 | make db-run-snippet DB=mysql FILE=snippets/databases/mysql_example.py 35 | ``` 36 | 37 | ### 3. Tắt Database khi không sử dụng 38 | 39 | ```bash 40 | # Sử dụng Docker Compose trực tiếp 41 | docker-compose -f docker/environments/databases/docker-compose.yml stop mysql 42 | 43 | # Hoặc sử dụng Makefile 44 | make db-stop DB=mysql 45 | ``` 46 | 47 | ## Thông Tin Kết Nối Database 48 | 49 | Khi chạy code snippet với database, các biến môi trường sau sẽ được tự động truyền vào container: 50 | 51 | - `DB_HOST`: Hostname của database 52 | - `DB_PORT`: Port của database 53 | - `DB_USER`: Username để kết nối 54 | - `DB_PASS`: Password để kết nối 55 | - `DB_NAME`: Tên database 56 | - `DB_CONN_STR`: Connection string đầy đủ 57 | 58 | ### Các Connection String Mặc Định 59 | 60 | - **MySQL**: `mysql://user:password@tech-notes-mysql:3306/tech_notes` 61 | - **PostgreSQL**: `postgresql://user:password@tech-notes-postgres:5432/tech_notes` 62 | - **MongoDB**: `mongodb://user:password@tech-notes-mongodb:27017/tech_notes` 63 | - **Redis**: `redis://tech-notes-redis:6379` 64 | - **SQLite**: `sqlite:///data/tech_notes.db` 65 | 66 | ### Cấu Hình Biến Môi Trường 67 | 68 | Để thay đổi các thông tin kết nối mặc định, bạn có thể tạo một file `.env` trong thư mục `docker/environments/databases/`: 69 | 70 | 1. Sao chép file `.env.example` thành `.env`: 71 | ```bash 72 | cp docker/environments/databases/.env.example docker/environments/databases/.env 73 | ``` 74 | 75 | 2. Chỉnh sửa file `.env` để thay đổi các thông tin kết nối (tên người dùng, mật khẩu, tên database, v.v.) 76 | 77 | 3. Khi chạy lệnh `run-db-snippet.sh` hoặc `make db-run-snippet`, các biến môi trường từ file `.env` sẽ được tự động sử dụng. 78 | 79 | Lưu ý: File `.env` đã được thêm vào `.gitignore` để không theo dõi bởi Git, đảm bảo thông tin nhạy cảm không bị đưa lên repository. 80 | 81 | ## Ví Dụ Code Kết Nối 82 | 83 | ### Python với MySQL 84 | 85 | ```python 86 | import os 87 | import mysql.connector 88 | 89 | # Lấy thông tin kết nối từ biến môi trường 90 | db_host = os.environ.get('DB_HOST', 'localhost') 91 | db_port = os.environ.get('DB_PORT', '3306') 92 | db_user = os.environ.get('DB_USER', 'user') 93 | db_pass = os.environ.get('DB_PASS', 'password') 94 | db_name = os.environ.get('DB_NAME', 'tech_notes') 95 | 96 | # Kết nối tới database 97 | conn = mysql.connector.connect( 98 | host=db_host, 99 | port=db_port, 100 | user=db_user, 101 | password=db_pass, 102 | database=db_name 103 | ) 104 | 105 | cursor = conn.cursor() 106 | cursor.execute("SELECT * FROM users") 107 | users = cursor.fetchall() 108 | 109 | for user in users: 110 | print(user) 111 | 112 | conn.close() 113 | ``` 114 | 115 | ### JavaScript với MongoDB 116 | 117 | ```javascript 118 | const { MongoClient } = require('mongodb'); 119 | 120 | // Lấy connection string từ biến môi trường 121 | const uri = process.env.DB_CONN_STR || 'mongodb://user:password@localhost:27017/tech_notes'; 122 | 123 | async function main() { 124 | const client = new MongoClient(uri); 125 | 126 | try { 127 | await client.connect(); 128 | const database = client.db('tech_notes'); 129 | const users = database.collection('users'); 130 | 131 | const query = {}; 132 | const cursor = users.find(query); 133 | 134 | if ((await cursor.count()) === 0) { 135 | console.log("No documents found!"); 136 | } 137 | 138 | await cursor.forEach(user => { 139 | console.log(user); 140 | }); 141 | 142 | } finally { 143 | await client.close(); 144 | } 145 | } 146 | 147 | main().catch(console.error); 148 | ``` 149 | 150 | ## Dữ Liệu Mẫu 151 | 152 | Mỗi database đã được cấu hình với dữ liệu mẫu sau: 153 | 154 | - Bảng/Collection `users` với 3 người dùng 155 | - Bảng/Collection `posts` với 4 bài viết liên kết với người dùng 156 | 157 | Bạn có thể xem chi tiết dữ liệu mẫu trong các file init trong thư mục tương ứng của mỗi database. 158 | -------------------------------------------------------------------------------- /docker/QUICK_START.md: -------------------------------------------------------------------------------- 1 | # Docker Quick Start Guide 2 | 3 | This guide will help you get started with running code snippets in Docker environments. 4 | 5 | ## Prerequisites 6 | 7 | 1. Docker installed on your machine 8 | 2. Docker Compose installed on your machine 9 | 10 | ## Getting Started 11 | 12 | ### 1. Build the Docker Environments 13 | 14 | First, build all the Docker environments: 15 | 16 | ```bash 17 | # Using Docker Compose directly 18 | docker-compose build 19 | 20 | # Or using the Makefile 21 | make build 22 | ``` 23 | 24 | ### 2. Run a Code Snippet 25 | 26 | You can run any code snippet from the repository using the provided script: 27 | 28 | ```bash 29 | # Using the script directly 30 | ./docker/run-snippet.sh snippets/algorithms/graph-traversal/graph_traversal.py 31 | 32 | # Or using the Makefile 33 | make run-snippet FILE=snippets/algorithms/graph-traversal/graph_traversal.py 34 | ``` 35 | 36 | The script automatically detects the file extension and uses the appropriate Docker container for the language. 37 | 38 | ### 3. Running Different Languages 39 | 40 | The setup supports multiple programming languages: 41 | 42 | - **Python**: `.py` files 43 | - **JavaScript**: `.js` files 44 | - **Java**: `.java` files 45 | - **C/C++**: `.c` and `.cpp` files 46 | - **Go**: `.go` files 47 | - **Rust**: `.rs` files 48 | - **PHP**: `.php` files 49 | - **C#**: `.cs` files 50 | - **Ruby**: `.rb` files 51 | 52 | ### 4. Troubleshooting 53 | 54 | If you encounter any issues: 55 | 56 | 1. Make sure Docker and Docker Compose are installed and running 57 | 2. Verify that the Docker daemon is running 58 | 3. Check the file path provided to the run-snippet script 59 | 4. Ensure the file extension is supported 60 | 61 | For more detailed information, see the [Docker README](README.md). 62 | -------------------------------------------------------------------------------- /docker/QUICK_START_vi.md: -------------------------------------------------------------------------------- 1 | # Hướng Dẫn Nhanh Docker 2 | 3 | Hướng dẫn này sẽ giúp bạn bắt đầu chạy các đoạn mã trong môi trường Docker. 4 | 5 | ## Yêu Cầu Tiên Quyết 6 | 7 | 1. Docker đã được cài đặt trên máy của bạn 8 | 2. Docker Compose đã được cài đặt trên máy của bạn 9 | 10 | ## Bắt Đầu 11 | 12 | ### 1. Xây Dựng Môi Trường Docker 13 | 14 | Đầu tiên, xây dựng tất cả các môi trường Docker: 15 | 16 | ```bash 17 | # Sử dụng Docker Compose trực tiếp 18 | docker-compose build 19 | 20 | # Hoặc sử dụng Makefile 21 | make build 22 | ``` 23 | 24 | ### 2. Chạy Một Đoạn Mã 25 | 26 | Bạn có thể chạy bất kỳ đoạn mã nào từ kho lưu trữ bằng script được cung cấp: 27 | 28 | ```bash 29 | # Sử dụng script trực tiếp 30 | ./docker/run-snippet.sh snippets/algorithms/graph-traversal/graph_traversal.py 31 | 32 | # Hoặc sử dụng Makefile 33 | make run-snippet FILE=snippets/algorithms/graph-traversal/graph_traversal.py 34 | ``` 35 | 36 | Script tự động phát hiện phần mở rộng tệp và sử dụng container Docker thích hợp cho ngôn ngữ đó. 37 | 38 | ### 3. Chạy Các Ngôn Ngữ Khác Nhau 39 | 40 | Thiết lập hỗ trợ nhiều ngôn ngữ lập trình: 41 | 42 | - **Python**: Tệp `.py` 43 | - **JavaScript**: Tệp `.js` 44 | - **Java**: Tệp `.java` 45 | - **C/C++**: Tệp `.c` và `.cpp` 46 | - **Go**: Tệp `.go` 47 | - **Rust**: Tệp `.rs` 48 | - **PHP**: Tệp `.php` 49 | - **C#**: Tệp `.cs` 50 | - **Ruby**: Tệp `.rb` 51 | 52 | ### 4. Xử Lý Sự Cố 53 | 54 | Nếu bạn gặp bất kỳ vấn đề nào: 55 | 56 | 1. Đảm bảo Docker và Docker Compose đã được cài đặt và đang chạy 57 | 2. Xác minh rằng Docker daemon đang chạy 58 | 3. Kiểm tra đường dẫn tệp được cung cấp cho script run-snippet 59 | 4. Đảm bảo phần mở rộng tệp được hỗ trợ 60 | 61 | Để biết thông tin chi tiết hơn, hãy xem [Docker README](README_vi.md). 62 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker Environment for Tech Notes Hub 2 | 3 | This directory contains Docker configurations for running code snippets in various programming languages and environments. 4 | 5 | ## Directory Structure 6 | 7 | - `environments/` - Contains Dockerfiles and setup scripts for each language 8 | - `python/` - Python environment 9 | - `javascript/` - JavaScript/Node.js environment 10 | - `java/` - Java environment 11 | - `cpp/` - C/C++ environment 12 | - `go/` - Go environment 13 | - `rust/` - Rust environment 14 | - `php/` - PHP environment 15 | - `csharp/` - C# environment 16 | - `ruby/` - Ruby environment 17 | - `databases/` - Database environments 18 | - `mysql/` - MySQL environment 19 | - `postgresql/` - PostgreSQL environment 20 | - `mongodb/` - MongoDB environment 21 | - `redis/` - Redis environment 22 | - `sqlite/` - SQLite environment 23 | - `shell/` - Shell environment for Linux scripts and DevOps 24 | - `databricks/` - Databricks/PySpark environment for data processing 25 | 26 | ## Usage 27 | 28 | ### Running a Snippet 29 | 30 | Use the provided `run-snippet.sh` script to run a code snippet in the appropriate Docker environment: 31 | 32 | ```bash 33 | ./docker/run-snippet.sh snippets/algorithms/graph-traversal/graph_traversal.py 34 | ``` 35 | 36 | The script automatically detects the file extension and uses the appropriate Docker container. 37 | 38 | ### Running a Snippet with Database Connection 39 | 40 | Use the provided `run-db-snippet.sh` script to run a code snippet with a database connection: 41 | 42 | ```bash 43 | ./docker/run-db-snippet.sh mysql snippets/databases/mysql_example.py 44 | ``` 45 | 46 | The script starts the specified database, connects it to your code environment, and runs the code with the appropriate connection parameters. 47 | 48 | For more information on using databases, see the [Database Guide](DATABASE_GUIDE.md). 49 | 50 | ### Building All Environments 51 | 52 | To build all Docker environments without running them: 53 | 54 | ```bash 55 | docker-compose build 56 | ``` 57 | 58 | ### Running a Specific Environment 59 | 60 | To run a specific environment: 61 | 62 | ```bash 63 | docker-compose run --rm python snippets/path/to/your/script.py 64 | docker-compose run --rm javascript snippets/path/to/your/script.js 65 | docker-compose run --rm java snippets/path/to/your/script.java 66 | # etc. 67 | ``` 68 | 69 | ### Database Operations 70 | 71 | To start a specific database: 72 | 73 | ```bash 74 | docker-compose -f docker/environments/databases/docker-compose.yml up -d mysql 75 | # Or using the Makefile 76 | make db-start DB=mysql 77 | ``` 78 | 79 | To stop a database: 80 | 81 | ```bash 82 | docker-compose -f docker/environments/databases/docker-compose.yml stop mysql 83 | # Or using the Makefile 84 | make db-stop DB=mysql 85 | ``` 86 | 87 | ### Database Environment Variables 88 | 89 | The database configurations use environment variables for sensitive information. A `.env.example` file is provided in the `docker/environments/databases/` directory as a template. 90 | 91 | To use custom database credentials: 92 | 93 | 1. Copy the example file to create a `.env` file: 94 | ```bash 95 | cp docker/environments/databases/.env.example docker/environments/databases/.env 96 | ``` 97 | 98 | 2. Edit the `.env` file to set your own credentials. 99 | 100 | 3. The `.env` file is included in `.gitignore` to ensure sensitive data isn't committed to the repository. 101 | 102 | ## Adding New Languages 103 | 104 | To add support for a new language: 105 | 106 | 1. Create a new directory in `environments/` for your language 107 | 2. Add a `Dockerfile` for the language 108 | 3. If needed, add an `entrypoint.sh` script for handling compilation/execution 109 | 4. Update the `docker-compose.yml` file to include your new service 110 | 5. Update the `run-snippet.sh` script to recognize the new file extension 111 | -------------------------------------------------------------------------------- /docker/README_vi.md: -------------------------------------------------------------------------------- 1 | # Môi Trường Docker cho Tech Notes Hub 2 | 3 | Thư mục này chứa các cấu hình Docker để chạy các đoạn mã trong nhiều ngôn ngữ lập trình và môi trường khác nhau. 4 | 5 | ## Cấu Trúc Thư Mục 6 | 7 | - `environments/` - Chứa Dockerfiles và scripts cài đặt cho mỗi ngôn ngữ 8 | - `python/` - Môi trường Python 9 | - `javascript/` - Môi trường JavaScript/Node.js 10 | - `java/` - Môi trường Java 11 | - `cpp/` - Môi trường C/C++ 12 | - `go/` - Môi trường Go 13 | - `rust/` - Môi trường Rust 14 | - `php/` - Môi trường PHP 15 | - `csharp/` - Môi trường C# 16 | - `ruby/` - Môi trường Ruby 17 | - `databases/` - Môi trường Cơ sở dữ liệu 18 | - `mysql/` - Môi trường MySQL 19 | - `postgresql/` - Môi trường PostgreSQL 20 | - `mongodb/` - Môi trường MongoDB 21 | - `redis/` - Môi trường Redis 22 | - `sqlite/` - Môi trường SQLite 23 | - `shell/` - Môi trường Shell cho scripts Linux và DevOps 24 | - `databricks/` - Môi trường Databricks/PySpark cho xử lý dữ liệu 25 | 26 | ## Cách Sử Dụng 27 | 28 | ### Chạy Một Đoạn Mã 29 | 30 | Sử dụng script `run-snippet.sh` được cung cấp để chạy một đoạn mã trong môi trường Docker phù hợp: 31 | 32 | ```bash 33 | ./docker/run-snippet.sh snippets/algorithms/graph-traversal/graph_traversal.py 34 | ``` 35 | 36 | Script tự động phát hiện phần mở rộng tệp và sử dụng container Docker thích hợp. 37 | 38 | ### Chạy Đoạn Mã với Kết Nối Cơ Sở Dữ Liệu 39 | 40 | Sử dụng script `run-db-snippet.sh` được cung cấp để chạy một đoạn mã với kết nối cơ sở dữ liệu: 41 | 42 | ```bash 43 | ./docker/run-db-snippet.sh mysql snippets/databases/mysql_example.py 44 | ``` 45 | 46 | Script sẽ khởi động cơ sở dữ liệu được chỉ định, kết nối nó với môi trường mã của bạn, và chạy mã với các tham số kết nối thích hợp. 47 | 48 | Để biết thêm thông tin về việc sử dụng cơ sở dữ liệu, xem [Hướng Dẫn Cơ Sở Dữ Liệu](DATABASE_GUIDE_vi.md). 49 | 50 | ### Xây Dựng Tất Cả Các Môi Trường 51 | 52 | Để xây dựng tất cả các môi trường Docker mà không chạy chúng: 53 | 54 | ```bash 55 | docker-compose build 56 | ``` 57 | 58 | ### Chạy Một Môi Trường Cụ Thể 59 | 60 | Để chạy một môi trường cụ thể: 61 | 62 | ```bash 63 | docker-compose run --rm python snippets/path/to/your/script.py 64 | docker-compose run --rm javascript snippets/path/to/your/script.js 65 | docker-compose run --rm java snippets/path/to/your/script.java 66 | # v.v. 67 | ``` 68 | 69 | ### Thao Tác Cơ Sở Dữ Liệu 70 | 71 | Để khởi động một cơ sở dữ liệu cụ thể: 72 | 73 | ```bash 74 | docker-compose -f docker/environments/databases/docker-compose.yml up -d mysql 75 | # Hoặc sử dụng Makefile 76 | make db-start DB=mysql 77 | ``` 78 | 79 | Để dừng một cơ sở dữ liệu: 80 | 81 | ```bash 82 | docker-compose -f docker/environments/databases/docker-compose.yml stop mysql 83 | # Hoặc sử dụng Makefile 84 | make db-stop DB=mysql 85 | ``` 86 | 87 | ### Biến Môi Trường Cơ Sở Dữ Liệu 88 | 89 | Cấu hình cơ sở dữ liệu sử dụng biến môi trường cho thông tin nhạy cảm. Một file `.env.example` được cung cấp trong thư mục `docker/environments/databases/` làm mẫu. 90 | 91 | Để sử dụng thông tin đăng nhập cơ sở dữ liệu tùy chỉnh: 92 | 93 | 1. Sao chép file mẫu để tạo file `.env`: 94 | ```bash 95 | cp docker/environments/databases/.env.example docker/environments/databases/.env 96 | ``` 97 | 98 | 2. Chỉnh sửa file `.env` để đặt thông tin đăng nhập của riêng bạn. 99 | 100 | 3. File `.env` được bao gồm trong `.gitignore` để đảm bảo dữ liệu nhạy cảm không được commit vào repository. 101 | 102 | ## Thêm Ngôn Ngữ Mới 103 | 104 | Để thêm hỗ trợ cho một ngôn ngữ mới: 105 | 106 | 1. Tạo một thư mục mới trong `environments/` cho ngôn ngữ của bạn 107 | 2. Thêm một `Dockerfile` cho ngôn ngữ đó 108 | 3. Nếu cần, thêm một script `entrypoint.sh` để xử lý biên dịch/thực thi 109 | 4. Cập nhật tệp `docker-compose.yml` để bao gồm dịch vụ mới của bạn 110 | 5. Cập nhật script `run-snippet.sh` để nhận diện phần mở rộng tệp mới 111 | -------------------------------------------------------------------------------- /docker/environments/cpp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:12 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary development tools 6 | RUN apt-get update && apt-get install -y \ 7 | cmake \ 8 | make \ 9 | gdb \ 10 | valgrind \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | # Set up compilation and execution command 14 | COPY docker/environments/cpp/entrypoint.sh /entrypoint.sh 15 | RUN chmod +x /entrypoint.sh 16 | 17 | ENTRYPOINT ["/entrypoint.sh"] 18 | -------------------------------------------------------------------------------- /docker/environments/cpp/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | FILE=$1 5 | FILENAME=$(basename "$FILE") 6 | EXTENSION="${FILENAME##*.}" 7 | OUTPUT_NAME="${FILENAME%.*}" 8 | 9 | echo "Compiling $FILENAME..." 10 | if [ "$EXTENSION" == "c" ]; then 11 | gcc -o "$OUTPUT_NAME" "$FILE" -lm 12 | elif [ "$EXTENSION" == "cpp" ]; then 13 | g++ -o "$OUTPUT_NAME" "$FILE" -std=c++17 14 | else 15 | echo "Unsupported file extension: $EXTENSION" 16 | exit 1 17 | fi 18 | 19 | echo "Running $OUTPUT_NAME..." 20 | ./"$OUTPUT_NAME" 21 | -------------------------------------------------------------------------------- /docker/environments/csharp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:7.0 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary .NET tools 6 | RUN dotnet tool install -g dotnet-format && \ 7 | dotnet tool install -g dotnet-trace && \ 8 | dotnet tool install -g dotnet-counters 9 | 10 | # Add .NET tools to PATH 11 | ENV PATH="$PATH:/root/.dotnet/tools" 12 | 13 | # Set up compilation and execution command 14 | COPY docker/environments/csharp/entrypoint.sh /entrypoint.sh 15 | RUN chmod +x /entrypoint.sh 16 | 17 | ENTRYPOINT ["/entrypoint.sh"] 18 | -------------------------------------------------------------------------------- /docker/environments/csharp/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | FILE=$1 5 | FILENAME=$(basename "$FILE") 6 | OUTPUT_NAME="${FILENAME%.*}" 7 | 8 | echo "Compiling $FILENAME..." 9 | csc /out:"$OUTPUT_NAME.exe" "$FILE" 10 | 11 | echo "Running $OUTPUT_NAME.exe..." 12 | mono "$OUTPUT_NAME.exe" 13 | -------------------------------------------------------------------------------- /docker/environments/databases/.env.example: -------------------------------------------------------------------------------- 1 | # MySQL Configuration 2 | MYSQL_ROOT_PASSWORD=rootpassword 3 | MYSQL_DATABASE=tech_notes 4 | MYSQL_USER=user 5 | MYSQL_PASSWORD=password 6 | MYSQL_PORT_EXPOSE=3306 7 | MYSQL_PORT=3306 8 | 9 | # PostgreSQL Configuration 10 | POSTGRES_USER=user 11 | POSTGRES_PASSWORD=password 12 | POSTGRES_DB=tech_notes 13 | POSTGRES_PORT_EXPOSE=5432 14 | POSTGRES_PORT=5432 15 | 16 | # MongoDB Configuration 17 | MONGO_INITDB_ROOT_USERNAME=root 18 | MONGO_INITDB_ROOT_PASSWORD=rootpassword 19 | MONGO_INITDB_DATABASE=tech_notes 20 | MONGO_USER=user 21 | MONGO_PASSWORD=password 22 | MONGO_PORT_EXPOSE=27017 23 | MONGO_PORT=27017 24 | 25 | # Redis Configuration 26 | REDIS_PASSWORD=your_secure_password 27 | REDIS_PORT_EXPOSE=6379 28 | REDIS_PORT=6379 29 | REDIS_PASSWORD=your_secure_password 30 | 31 | # SQLite Configuration 32 | # SQLite does not require credentials 33 | 34 | # MongoDB 35 | MONGODB_PORT_EXPOSE=27017 36 | MONGODB_PORT=27017 37 | MONGODB_DATABASE=tech_notes 38 | MONGODB_USER=root 39 | MONGODB_PASSWORD=your_secure_password -------------------------------------------------------------------------------- /docker/environments/databases/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | # MySQL 5 | mysql: 6 | image: mysql:8.0 7 | container_name: tech-notes-mysql 8 | environment: 9 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} 10 | MYSQL_DATABASE: ${MYSQL_DATABASE:-tech_notes} 11 | MYSQL_USER: ${MYSQL_USER:-user} 12 | MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password} 13 | ports: 14 | - "${MYSQL_PORT_EXPOSE:-3306}:${MYSQL_PORT:-3306}" 15 | volumes: 16 | - mysql_data:/var/lib/mysql 17 | - ./mysql/init:/docker-entrypoint-initdb.d 18 | restart: unless-stopped 19 | command: --default-authentication-plugin=mysql_native_password 20 | 21 | # PostgreSQL 22 | postgres: 23 | image: postgres:15 24 | container_name: tech-notes-postgres 25 | environment: 26 | POSTGRES_USER: ${POSTGRES_USER:-user} 27 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} 28 | POSTGRES_DB: ${POSTGRES_DB:-tech_notes} 29 | ports: 30 | - "${POSTGRES_PORT_EXPOSE:-5432}:${POSTGRES_PORT:-5432}" 31 | volumes: 32 | - postgres_data:/var/lib/postgresql/data 33 | - ./postgresql/init:/docker-entrypoint-initdb.d 34 | restart: unless-stopped 35 | 36 | # MongoDB 37 | mongodb: 38 | image: mongo:6 39 | container_name: tech-notes-mongodb 40 | environment: 41 | MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME:-root} 42 | MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD:-rootpassword} 43 | MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE:-tech_notes} 44 | ports: 45 | - "${MONGO_PORT_EXPOSE:-27017}:${MONGO_PORT:-27017}" 46 | volumes: 47 | - mongodb_data:/data/db 48 | - ./mongodb/init:/docker-entrypoint-initdb.d 49 | restart: unless-stopped 50 | 51 | # Redis 52 | redis: 53 | image: redis:7 54 | container_name: tech-notes-redis 55 | ports: 56 | - "${REDIS_PORT_EXPOSE:-6379}:${REDIS_PORT:-6379}" 57 | volumes: 58 | - redis_data:/data 59 | - ./redis/redis.conf:/usr/local/etc/redis/redis.conf 60 | command: redis-server /usr/local/etc/redis/redis.conf 61 | restart: unless-stopped 62 | 63 | # SQLite 64 | sqlite: 65 | build: ./sqlite 66 | container_name: tech-notes-sqlite 67 | volumes: 68 | - sqlite_data:/data 69 | restart: unless-stopped 70 | 71 | volumes: 72 | mysql_data: 73 | postgres_data: 74 | mongodb_data: 75 | redis_data: 76 | sqlite_data: 77 | -------------------------------------------------------------------------------- /docker/environments/databases/mongodb/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | mongodb: 5 | image: mongo:6 6 | container_name: tech-notes-mongodb 7 | environment: 8 | MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USER:-root} 9 | MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD:-rootpassword} 10 | MONGO_INITDB_DATABASE: ${MONGODB_DATABASE:-tech_notes} 11 | ports: 12 | - "${MONGODB_PORT_EXPOSE:-27017}:${MONGODB_PORT:-27017}" 13 | volumes: 14 | - mongodb_data:/data/db 15 | - ./init:/docker-entrypoint-initdb.d 16 | restart: unless-stopped 17 | 18 | volumes: 19 | mongodb_data: 20 | -------------------------------------------------------------------------------- /docker/environments/databases/mongodb/init/01-sample-data.js: -------------------------------------------------------------------------------- 1 | // Create a user for the database 2 | db.createUser({ 3 | user: 'user', 4 | pwd: 'password', 5 | roles: [ 6 | { 7 | role: 'readWrite', 8 | db: 'tech_notes' 9 | } 10 | ] 11 | }); 12 | 13 | // Switch to the tech_notes database 14 | db = db.getSiblingDB('tech_notes'); 15 | 16 | // Create collections 17 | db.createCollection('users'); 18 | db.createCollection('posts'); 19 | 20 | // Insert sample data into users collection 21 | db.users.insertMany([ 22 | { 23 | username: 'user1', 24 | email: 'user1@example.com', 25 | created_at: new Date() 26 | }, 27 | { 28 | username: 'user2', 29 | email: 'user2@example.com', 30 | created_at: new Date() 31 | }, 32 | { 33 | username: 'user3', 34 | email: 'user3@example.com', 35 | created_at: new Date() 36 | } 37 | ]); 38 | 39 | // Get the user IDs 40 | const user1 = db.users.findOne({ username: 'user1' }); 41 | const user2 = db.users.findOne({ username: 'user2' }); 42 | const user3 = db.users.findOne({ username: 'user3' }); 43 | 44 | // Insert sample data into posts collection 45 | db.posts.insertMany([ 46 | { 47 | title: 'First Post', 48 | content: 'This is the content of the first post', 49 | user_id: user1._id, 50 | created_at: new Date() 51 | }, 52 | { 53 | title: 'Second Post', 54 | content: 'This is the content of the second post', 55 | user_id: user1._id, 56 | created_at: new Date() 57 | }, 58 | { 59 | title: 'Hello World', 60 | content: 'Hello world post content', 61 | user_id: user2._id, 62 | created_at: new Date() 63 | }, 64 | { 65 | title: 'Database Demo', 66 | content: 'This is a demonstration of MongoDB database', 67 | user_id: user3._id, 68 | created_at: new Date() 69 | } 70 | ]); 71 | -------------------------------------------------------------------------------- /docker/environments/databases/mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | mysql: 5 | image: mysql:8.0 6 | container_name: tech-notes-mysql 7 | environment: 8 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} 9 | MYSQL_DATABASE: ${MYSQL_DATABASE:-tech_notes} 10 | MYSQL_USER: ${MYSQL_USER:-user} 11 | MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password} 12 | ports: 13 | - "${MYSQL_PORT_EXPOSE:-3306}:${MYSQL_PORT:-3306}" 14 | volumes: 15 | - mysql_data:/var/lib/mysql 16 | - ./init:/docker-entrypoint-initdb.d 17 | restart: unless-stopped 18 | command: --default-authentication-plugin=mysql_native_password 19 | 20 | volumes: 21 | mysql_data: 22 | -------------------------------------------------------------------------------- /docker/environments/databases/mysql/init/01-sample-data.sql: -------------------------------------------------------------------------------- 1 | -- Create sample tables 2 | CREATE TABLE IF NOT EXISTS users ( 3 | id INT AUTO_INCREMENT PRIMARY KEY, 4 | username VARCHAR(50) NOT NULL UNIQUE, 5 | email VARCHAR(100) NOT NULL UNIQUE, 6 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 7 | ); 8 | 9 | CREATE TABLE IF NOT EXISTS posts ( 10 | id INT AUTO_INCREMENT PRIMARY KEY, 11 | title VARCHAR(255) NOT NULL, 12 | content TEXT, 13 | user_id INT, 14 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 15 | FOREIGN KEY (user_id) REFERENCES users(id) 16 | ); 17 | 18 | -- Insert sample data 19 | INSERT INTO users (username, email) VALUES 20 | ('user1', 'user1@example.com'), 21 | ('user2', 'user2@example.com'), 22 | ('user3', 'user3@example.com'); 23 | 24 | INSERT INTO posts (title, content, user_id) VALUES 25 | ('First Post', 'This is the content of the first post', 1), 26 | ('Second Post', 'This is the content of the second post', 1), 27 | ('Hello World', 'Hello world post content', 2), 28 | ('Database Demo', 'This is a demonstration of MySQL database', 3); 29 | -------------------------------------------------------------------------------- /docker/environments/databases/postgresql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | postgres: 5 | image: postgres:15 6 | container_name: tech-notes-postgres 7 | environment: 8 | POSTGRES_USER: ${POSTGRES_USER:-user} 9 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} 10 | POSTGRES_DB: ${POSTGRES_DB:-tech_notes} 11 | ports: 12 | - "${POSTGRES_PORT_EXPOSE:-5432}:${POSTGRES_PORT:-5432}" 13 | volumes: 14 | - postgres_data:/var/lib/postgresql/data 15 | - ./init:/docker-entrypoint-initdb.d 16 | restart: unless-stopped 17 | 18 | volumes: 19 | postgres_data: 20 | -------------------------------------------------------------------------------- /docker/environments/databases/postgresql/init/01-sample-data.sql: -------------------------------------------------------------------------------- 1 | -- Create sample tables 2 | CREATE TABLE IF NOT EXISTS users ( 3 | id SERIAL PRIMARY KEY, 4 | username VARCHAR(50) NOT NULL UNIQUE, 5 | email VARCHAR(100) NOT NULL UNIQUE, 6 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 7 | ); 8 | 9 | CREATE TABLE IF NOT EXISTS posts ( 10 | id SERIAL PRIMARY KEY, 11 | title VARCHAR(255) NOT NULL, 12 | content TEXT, 13 | user_id INTEGER, 14 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 15 | FOREIGN KEY (user_id) REFERENCES users(id) 16 | ); 17 | 18 | -- Insert sample data 19 | INSERT INTO users (username, email) VALUES 20 | ('user1', 'user1@example.com'), 21 | ('user2', 'user2@example.com'), 22 | ('user3', 'user3@example.com'); 23 | 24 | INSERT INTO posts (title, content, user_id) VALUES 25 | ('First Post', 'This is the content of the first post', 1), 26 | ('Second Post', 'This is the content of the second post', 1), 27 | ('Hello World', 'Hello world post content', 2), 28 | ('Database Demo', 'This is a demonstration of PostgreSQL database', 3); 29 | -------------------------------------------------------------------------------- /docker/environments/databases/redis/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | redis: 5 | image: redis:7 6 | container_name: tech-notes-redis 7 | ports: 8 | - "${REDIS_PORT_EXPOSE:-6379}:${REDIS_PORT:-6379}" 9 | volumes: 10 | - redis_data:/data 11 | - ./redis.conf:/usr/local/etc/redis/redis.conf 12 | command: redis-server /usr/local/etc/redis/redis.conf 13 | restart: unless-stopped 14 | 15 | volumes: 16 | redis_data: 17 | -------------------------------------------------------------------------------- /docker/environments/databases/redis/redis.conf: -------------------------------------------------------------------------------- 1 | # Redis configuration file 2 | 3 | # Basic configuration 4 | bind 0.0.0.0 5 | protected-mode yes 6 | port ${REDIS_PORT:-6379} 7 | tcp-backlog 511 8 | timeout 0 9 | tcp-keepalive 300 10 | 11 | # General configuration 12 | daemonize no 13 | supervised no 14 | pidfile /var/run/redis_${REDIS_PORT:-6379}.pid 15 | loglevel notice 16 | logfile "" 17 | databases 16 18 | 19 | # Snapshotting 20 | save 900 1 21 | save 300 10 22 | save 60 10000 23 | stop-writes-on-bgsave-error yes 24 | rdbcompression yes 25 | rdbchecksum yes 26 | dbfilename dump.rdb 27 | dir /data 28 | 29 | # Security (no password by default for easy access) 30 | # Comment this to disable password 31 | requirepass ${REDIS_PASSWORD:-your_secure_password} 32 | 33 | # Memory management 34 | maxmemory 256mb 35 | maxmemory-policy allkeys-lru 36 | 37 | # Append only mode 38 | appendonly yes 39 | appendfilename "appendonly.aof" 40 | appendfsync everysec 41 | no-appendfsync-on-rewrite no 42 | auto-aof-rewrite-percentage 100 43 | auto-aof-rewrite-min-size 64mb 44 | aof-load-truncated yes 45 | 46 | # Lua scripting 47 | lua-time-limit 5000 48 | 49 | # Slow log 50 | slowlog-log-slower-than 10000 51 | slowlog-max-len 128 52 | -------------------------------------------------------------------------------- /docker/environments/databases/sqlite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.17 2 | 3 | # Install SQLite and required tools 4 | RUN apk add --no-cache sqlite sqlite-dev sqlite-libs bash 5 | 6 | # Create data directory 7 | RUN mkdir -p /data 8 | 9 | # Set working directory 10 | WORKDIR /data 11 | 12 | # Copy init script 13 | COPY init.sh /init.sh 14 | COPY init.sql /init.sql 15 | 16 | # Make the script executable 17 | RUN chmod +x /init.sh 18 | 19 | # Run init script on startup 20 | ENTRYPOINT ["/init.sh"] 21 | -------------------------------------------------------------------------------- /docker/environments/databases/sqlite/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | services: 4 | sqlite: 5 | build: . 6 | container_name: tech-notes-sqlite 7 | volumes: 8 | - sqlite_data:/data 9 | restart: unless-stopped 10 | 11 | volumes: 12 | sqlite_data: 13 | -------------------------------------------------------------------------------- /docker/environments/databases/sqlite/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create the database if it doesn't exist 4 | if [ ! -f /data/tech_notes.db ]; then 5 | echo "Creating new SQLite database..." 6 | sqlite3 /data/tech_notes.db < /init.sql 7 | echo "Database created and initialized." 8 | else 9 | echo "Database already exists." 10 | fi 11 | 12 | # Keep container running 13 | echo "SQLite container is ready. Database is at /data/tech_notes.db" 14 | echo "Use 'docker exec -it tech-notes-sqlite sqlite3 /data/tech_notes.db' to access the database." 15 | tail -f /dev/null 16 | -------------------------------------------------------------------------------- /docker/environments/databases/sqlite/init.sql: -------------------------------------------------------------------------------- 1 | -- Create sample tables 2 | CREATE TABLE IF NOT EXISTS users ( 3 | id INTEGER PRIMARY KEY AUTOINCREMENT, 4 | username TEXT NOT NULL UNIQUE, 5 | email TEXT NOT NULL UNIQUE, 6 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 7 | ); 8 | 9 | CREATE TABLE IF NOT EXISTS posts ( 10 | id INTEGER PRIMARY KEY AUTOINCREMENT, 11 | title TEXT NOT NULL, 12 | content TEXT, 13 | user_id INTEGER, 14 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 15 | FOREIGN KEY (user_id) REFERENCES users(id) 16 | ); 17 | 18 | -- Insert sample data 19 | INSERT INTO users (username, email) VALUES 20 | ('user1', 'user1@example.com'), 21 | ('user2', 'user2@example.com'), 22 | ('user3', 'user3@example.com'); 23 | 24 | INSERT INTO posts (title, content, user_id) VALUES 25 | ('First Post', 'This is the content of the first post', 1), 26 | ('Second Post', 'This is the content of the second post', 1), 27 | ('Hello World', 'Hello world post content', 2), 28 | ('Database Demo', 'This is a demonstration of SQLite database', 3); 29 | -------------------------------------------------------------------------------- /docker/environments/databricks/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | # Install Java 4 | RUN apt-get update && \ 5 | apt-get install -y openjdk-11-jdk && \ 6 | apt-get clean && \ 7 | rm -rf /var/lib/apt/lists/* 8 | 9 | # Set Java home 10 | ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 11 | 12 | # Install PySpark and Databricks libraries 13 | RUN pip install --no-cache-dir \ 14 | pyspark==3.3.0 \ 15 | delta-spark==2.1.0 \ 16 | databricks-connect==10.4.* \ 17 | matplotlib \ 18 | pandas \ 19 | numpy \ 20 | scikit-learn 21 | 22 | # Install additional libraries for databricks 23 | RUN pip install --no-cache-dir \ 24 | mlflow \ 25 | koalas \ 26 | plotly \ 27 | databricks-cli 28 | 29 | # Set up working directory 30 | WORKDIR /app 31 | 32 | # Create PySpark configuration directory 33 | RUN mkdir -p /root/.databricks-connect 34 | 35 | # Set entrypoint to Python 36 | ENTRYPOINT ["python"] 37 | -------------------------------------------------------------------------------- /docker/environments/go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21-alpine 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary Go packages 6 | RUN go install github.com/stretchr/testify@latest && \ 7 | go install github.com/golang/mock/mockgen@latest && \ 8 | go install golang.org/x/tools/cmd/goimports@latest 9 | 10 | # Set up command to run Go scripts 11 | ENTRYPOINT ["go", "run"] 12 | -------------------------------------------------------------------------------- /docker/environments/java/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:17-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install build tools 6 | RUN apt-get update && apt-get install -y \ 7 | maven \ 8 | gradle \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | # Set up compilation and execution command 12 | COPY docker/environments/java/entrypoint.sh /entrypoint.sh 13 | RUN chmod +x /entrypoint.sh 14 | 15 | ENTRYPOINT ["/entrypoint.sh"] 16 | -------------------------------------------------------------------------------- /docker/environments/java/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | FILE=$1 5 | FILENAME=$(basename "$FILE") 6 | CLASSNAME="${FILENAME%.*}" 7 | 8 | echo "Compiling $FILENAME..." 9 | javac "$FILE" 10 | 11 | echo "Running $CLASSNAME..." 12 | java -cp "$(dirname "$FILE")" "$CLASSNAME" 13 | -------------------------------------------------------------------------------- /docker/environments/javascript/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary Node.js packages 6 | RUN npm install -g \ 7 | jest \ 8 | mocha \ 9 | chai \ 10 | eslint 11 | 12 | # Set up command to run JavaScript scripts 13 | ENTRYPOINT ["node"] 14 | -------------------------------------------------------------------------------- /docker/environments/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-cli 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary PHP extensions and tools 6 | RUN apt-get update && apt-get install -y \ 7 | libzip-dev \ 8 | unzip \ 9 | git \ 10 | && docker-php-ext-install zip \ 11 | && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ 12 | && php composer-setup.php --install-dir=/usr/local/bin --filename=composer \ 13 | && php -r "unlink('composer-setup.php');" \ 14 | && rm -rf /var/lib/apt/lists/* 15 | 16 | # Install PHPUnit and other common packages 17 | RUN composer global require phpunit/phpunit 18 | 19 | # Add composer's vendor bin to PATH 20 | ENV PATH="/root/.composer/vendor/bin:${PATH}" 21 | 22 | # Set up command to run PHP scripts 23 | ENTRYPOINT ["php"] 24 | -------------------------------------------------------------------------------- /docker/environments/python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary Python packages 6 | RUN pip install --no-cache-dir \ 7 | pytest \ 8 | numpy \ 9 | pandas \ 10 | matplotlib \ 11 | scipy \ 12 | scikit-learn 13 | 14 | # Set up command to run Python scripts 15 | ENTRYPOINT ["python"] 16 | -------------------------------------------------------------------------------- /docker/environments/ruby/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.2-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary Ruby gems 6 | RUN gem install \ 7 | rspec \ 8 | rubocop \ 9 | pry \ 10 | sinatra \ 11 | rails 12 | 13 | # Set up command to run Ruby scripts 14 | ENTRYPOINT ["ruby"] 15 | -------------------------------------------------------------------------------- /docker/environments/rust/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.75-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install necessary Rust tools 6 | RUN rustup component add rustfmt clippy && \ 7 | cargo install cargo-watch cargo-expand 8 | 9 | # Set up command to run Rust scripts 10 | COPY docker/environments/rust/entrypoint.sh /entrypoint.sh 11 | RUN chmod +x /entrypoint.sh 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /docker/environments/rust/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | FILE=$1 5 | FILENAME=$(basename "$FILE") 6 | 7 | echo "Running $FILENAME with rustc..." 8 | rustc -o temp_executable "$FILE" && ./temp_executable 9 | rm -f temp_executable 10 | -------------------------------------------------------------------------------- /docker/environments/shell/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | # Install essential tools and utilities 4 | RUN apt-get update && apt-get install -y \ 5 | bash \ 6 | bc \ 7 | curl \ 8 | git \ 9 | iputils-ping \ 10 | iproute2 \ 11 | net-tools \ 12 | nodejs \ 13 | npm \ 14 | openssh-client \ 15 | procps \ 16 | systemd \ 17 | vim \ 18 | wget \ 19 | && rm -rf /var/lib/apt/lists/* 20 | 21 | # Set up working directory 22 | WORKDIR /app 23 | 24 | # Set bash as the default shell 25 | SHELL ["/bin/bash", "-c"] 26 | 27 | # Set entrypoint to bash shell 28 | ENTRYPOINT ["/bin/bash"] 29 | -------------------------------------------------------------------------------- /docker/redis/redis.conf: -------------------------------------------------------------------------------- 1 | # Security (no password by default for easy access) 2 | # uncomment and change to set password 3 | # requirepass yourpassword 4 | requirepass ${REDIS_PASSWORD} 5 | -------------------------------------------------------------------------------- /docker/run-db-snippet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -lt 2 ]; then 5 | echo "Usage: $0 " 6 | echo "Database types: mysql, postgres, mongodb, redis, sqlite" 7 | exit 1 8 | fi 9 | 10 | DB_TYPE="$1" 11 | SNIPPET_PATH="$2" 12 | EXTENSION="${SNIPPET_PATH##*.}" 13 | 14 | # Load environment variables from .env file if it exists 15 | ENV_FILE="docker/environments/databases/.env" 16 | if [ -f "$ENV_FILE" ]; then 17 | echo "Loading environment variables from $ENV_FILE" 18 | export $(grep -v '^#' "$ENV_FILE" | xargs) 19 | else 20 | echo "No .env file found at $ENV_FILE, using default values" 21 | fi 22 | 23 | # Start the selected database 24 | function start_database() { 25 | echo "Starting $DB_TYPE database..." 26 | docker-compose -f docker/environments/databases/docker-compose.yml up -d "$DB_TYPE" 27 | 28 | # Give some time for the database to start 29 | sleep 5 30 | 31 | echo "$DB_TYPE database is ready." 32 | } 33 | 34 | # Map file extensions to Docker services 35 | case "$EXTENSION" in 36 | py) 37 | SERVICE="python" 38 | ;; 39 | js) 40 | SERVICE="javascript" 41 | ;; 42 | java) 43 | SERVICE="java" 44 | ;; 45 | c|cpp) 46 | SERVICE="cpp" 47 | ;; 48 | go) 49 | SERVICE="go" 50 | ;; 51 | rs) 52 | SERVICE="rust" 53 | ;; 54 | php) 55 | SERVICE="php" 56 | ;; 57 | cs) 58 | SERVICE="csharp" 59 | ;; 60 | rb) 61 | SERVICE="ruby" 62 | ;; 63 | *) 64 | echo "Unsupported file extension: $EXTENSION" 65 | exit 1 66 | ;; 67 | esac 68 | 69 | # Get the database connection details 70 | case "$DB_TYPE" in 71 | mysql) 72 | DB_HOST="tech-notes-mysql" 73 | DB_PORT="${MYSQL_PORT_EXPOSE:-3306}" 74 | DB_USER="${MYSQL_USER:-user}" 75 | DB_PASS="${MYSQL_PASSWORD:-password}" 76 | DB_NAME="${MYSQL_DATABASE:-tech_notes}" 77 | DB_CONN_STR="mysql://$DB_USER:$DB_PASS@$DB_HOST:$DB_PORT/$DB_NAME" 78 | ;; 79 | postgres) 80 | DB_HOST="tech-notes-postgres" 81 | DB_PORT="${POSTGRES_PORT_EXPOSE:-5432}" 82 | DB_USER="${POSTGRES_USER:-user}" 83 | DB_PASS="${POSTGRES_PASSWORD:-password}" 84 | DB_NAME="${POSTGRES_DB:-tech_notes}" 85 | DB_CONN_STR="postgresql://$DB_USER:$DB_PASS@$DB_HOST:$DB_PORT/$DB_NAME" 86 | ;; 87 | mongodb) 88 | DB_HOST="tech-notes-mongodb" 89 | DB_PORT="${MONGO_PORT_EXPOSE:-27017}" 90 | DB_USER="${MONGO_USER:-user}" 91 | DB_PASS="${MONGO_PASSWORD:-password}" 92 | DB_NAME="${MONGO_INITDB_DATABASE:-tech_notes}" 93 | DB_CONN_STR="mongodb://$DB_USER:$DB_PASS@$DB_HOST:$DB_PORT/$DB_NAME" 94 | ;; 95 | redis) 96 | DB_HOST="tech-notes-redis" 97 | DB_PORT="${REDIS_PORT_EXPOSE:-6379}" 98 | DB_PASS="${REDIS_PASSWORD:-}" 99 | if [ -n "$DB_PASS" ]; then 100 | DB_CONN_STR="redis://:$DB_PASS@$DB_HOST:$DB_PORT" 101 | else 102 | DB_CONN_STR="redis://$DB_HOST:$DB_PORT" 103 | fi 104 | ;; 105 | sqlite) 106 | DB_CONN_STR="sqlite:///data/tech_notes.db" 107 | ;; 108 | *) 109 | echo "Unsupported database type: $DB_TYPE" 110 | exit 1 111 | ;; 112 | esac 113 | 114 | # Start the database 115 | start_database 116 | 117 | # Get absolute path from relative path 118 | ABSOLUTE_PATH=$(realpath "$SNIPPET_PATH") 119 | # Get the path relative to the project root 120 | RELATIVE_PATH=$(realpath --relative-to="$(pwd)" "$ABSOLUTE_PATH") 121 | 122 | # Create a custom docker network if it doesn't exist 123 | if ! docker network inspect tech-notes-network >/dev/null 2>&1; then 124 | echo "Creating docker network: tech-notes-network" 125 | docker network create tech-notes-network 126 | 127 | # Add the database container to the network 128 | echo "Adding $DB_TYPE database to the network" 129 | docker network connect tech-notes-network "tech-notes-$DB_TYPE" 130 | fi 131 | 132 | echo "Running $RELATIVE_PATH in $SERVICE environment with $DB_TYPE database..." 133 | docker-compose run --rm \ 134 | --network tech-notes-network \ 135 | -e DB_HOST="$DB_HOST" \ 136 | -e DB_PORT="$DB_PORT" \ 137 | -e DB_USER="$DB_USER" \ 138 | -e DB_PASS="$DB_PASS" \ 139 | -e DB_NAME="$DB_NAME" \ 140 | -e DB_CONN_STR="$DB_CONN_STR" \ 141 | "$SERVICE" "/app/$RELATIVE_PATH" 142 | -------------------------------------------------------------------------------- /docker/run-snippet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $# -lt 1 ]; then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | SNIPPET_PATH="$1" 10 | EXTENSION="${SNIPPET_PATH##*.}" 11 | 12 | # Map file extensions to Docker services 13 | case "$EXTENSION" in 14 | py) 15 | SERVICE="python" 16 | ;; 17 | js) 18 | SERVICE="javascript" 19 | ;; 20 | java) 21 | SERVICE="java" 22 | ;; 23 | c|cpp) 24 | SERVICE="cpp" 25 | ;; 26 | go) 27 | SERVICE="go" 28 | ;; 29 | rs) 30 | SERVICE="rust" 31 | ;; 32 | php) 33 | SERVICE="php" 34 | ;; 35 | cs) 36 | SERVICE="csharp" 37 | ;; 38 | rb) 39 | SERVICE="ruby" 40 | ;; 41 | sh) 42 | SERVICE="shell" 43 | ;; 44 | ipynb) 45 | SERVICE="databricks" 46 | ;; 47 | *) 48 | echo "Unsupported file extension: $EXTENSION" 49 | exit 1 50 | ;; 51 | esac 52 | 53 | # Get absolute path from relative path 54 | ABSOLUTE_PATH=$(realpath "$SNIPPET_PATH") 55 | # Get the path relative to the project root 56 | RELATIVE_PATH=$(realpath --relative-to="$(pwd)" "$ABSOLUTE_PATH") 57 | 58 | # Special handling for shell scripts 59 | if [ "$EXTENSION" == "sh" ]; then 60 | echo "Running $RELATIVE_PATH in $SERVICE environment..." 61 | # Make the script executable in the container and run it 62 | docker-compose run --rm "$SERVICE" -c "chmod +x /app/$RELATIVE_PATH && /app/$RELATIVE_PATH" 63 | elif [ "$EXTENSION" == "ipynb" ]; then 64 | echo "Running Jupyter notebook $RELATIVE_PATH in $SERVICE environment..." 65 | docker-compose run --rm "$SERVICE" -m jupyter nbconvert --execute --to notebook --inplace "/app/$RELATIVE_PATH" 66 | else 67 | echo "Running $RELATIVE_PATH in $SERVICE environment..." 68 | docker-compose run --rm "$SERVICE" "/app/$RELATIVE_PATH" 69 | fi 70 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub 2 | 3 | Welcome to the Tech Notes Hub! This repository is a comprehensive collection of technical notes, code snippets, and examples covering various topics in software development, system design, and computer science. 4 | 5 | ## Purpose 6 | 7 | Tech Notes Hub aims to: 8 | 9 | - Provide clear, concise explanations of important technical concepts 10 | - Offer practical code examples in multiple programming languages 11 | - Serve as a reference for developers at all skill levels 12 | - Create a collaborative knowledge base for the tech community 13 | 14 | ## Repository Structure 15 | 16 | The repository is organized into the following main sections: 17 | 18 | ### Algorithms 19 | Implementations and explanations of common algorithms, including search, sort, graph traversal, and more. 20 | 21 | ### Databases 22 | Notes on database systems, query optimization, data modeling, and best practices for both SQL and NoSQL databases. 23 | 24 | ### Design Patterns 25 | Detailed explanations and implementations of software design patterns across multiple programming languages. 26 | 27 | ### DevOps 28 | Information about continuous integration, deployment, containerization, and infrastructure management. 29 | 30 | ### Linux 31 | Guides for Linux system administration, shell scripting, and command-line tools. 32 | 33 | ### System Design 34 | Approaches to designing scalable, reliable, and maintainable software systems. 35 | 36 | ### Testing 37 | Best practices for unit testing, integration testing, and test-driven development. 38 | 39 | ## How to Use This Repository 40 | 41 | Each section contains markdown files with explanations and code snippets. You can: 42 | 43 | 1. Browse the sections to find topics of interest 44 | 2. Use the code examples as reference for your own projects 45 | 3. Contribute by adding new content or improving existing documentation 46 | 47 | ## Contributing 48 | 49 | We welcome contributions from the community! Please see our [CONTRIBUTING.md](../CONTRIBUTING.md) file for guidelines on how to contribute. 50 | 51 | ## License 52 | 53 | This project is licensed under the MIT License - see the [LICENSE.txt](../LICENSE.txt) file for details. 54 | -------------------------------------------------------------------------------- /docs/algorithms/sorting-algorithms.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Tech Notes Hub 3 | tags: 'learning, technology, programming' 4 | update: '2025-06-06' 5 | date: '2025-06-06' 6 | title: Sorting Algorithms 7 | description: Guide about Sorting Algorithms 8 | --- 9 | # Sorting Algorithms 10 | 11 | Sorting is one of the most fundamental operations in computer science. This article explores various sorting algorithms, their implementations, and performance characteristics. 12 | 13 | ## Quick Sort 14 | 15 | Quick Sort is a divide-and-conquer algorithm that works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays according to whether they are less than or greater than the pivot. 16 | 17 | ```python 18 | def quick_sort(arr): 19 | if len(arr) <= 1: 20 | return arr 21 | pivot = arr[len(arr) // 2] 22 | left = [x for x in arr if x < pivot] 23 | middle = [x for x in arr if x == pivot] 24 | right = [x for x in arr if x > pivot] 25 | return quick_sort(left) + middle + quick_sort(right) 26 | 27 | # Example usage 28 | arr = [3, 6, 8, 10, 1, 2, 1] 29 | print(quick_sort(arr)) # Output: [1, 1, 2, 3, 6, 8, 10] 30 | ``` 31 | 32 | **Time Complexity**: 33 | - Best Case: O(n log n) 34 | - Average Case: O(n log n) 35 | - Worst Case: O(n²) 36 | 37 | ## Merge Sort 38 | 39 | Merge Sort is another divide-and-conquer algorithm that divides the input array into two halves, recursively sorts them, and then merges the sorted halves. 40 | 41 | ```python 42 | def merge_sort(arr): 43 | if len(arr) <= 1: 44 | return arr 45 | 46 | mid = len(arr) // 2 47 | left = merge_sort(arr[:mid]) 48 | right = merge_sort(arr[mid:]) 49 | 50 | return merge(left, right) 51 | 52 | def merge(left, right): 53 | result = [] 54 | i = j = 0 55 | 56 | while i < len(left) and j < len(right): 57 | if left[i] < right[j]: 58 | result.append(left[i]) 59 | i += 1 60 | else: 61 | result.append(right[j]) 62 | j += 1 63 | 64 | result.extend(left[i:]) 65 | result.extend(right[j:]) 66 | return result 67 | ``` 68 | 69 | **Time Complexity**: 70 | - Best Case: O(n log n) 71 | - Average Case: O(n log n) 72 | - Worst Case: O(n log n) 73 | 74 | ## Bubble Sort 75 | 76 | Bubble Sort is a simple comparison-based algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. 77 | 78 | ```python 79 | def bubble_sort(arr): 80 | n = len(arr) 81 | for i in range(n): 82 | # Flag to optimize if array is already sorted 83 | swapped = False 84 | 85 | for j in range(0, n - i - 1): 86 | if arr[j] > arr[j + 1]: 87 | arr[j], arr[j + 1] = arr[j + 1], arr[j] 88 | swapped = True 89 | 90 | # If no swapping occurred in this pass, array is sorted 91 | if not swapped: 92 | break 93 | 94 | return arr 95 | ``` 96 | 97 | **Time Complexity**: 98 | - Best Case: O(n) - when array is already sorted 99 | - Average Case: O(n²) 100 | - Worst Case: O(n²) 101 | 102 | ## Comparison of Sorting Algorithms 103 | 104 | | Algorithm | Time Complexity (Best) | Time Complexity (Average) | Time Complexity (Worst) | Space Complexity | Stable | 105 | |-----------|------------------------|---------------------------|-------------------------|-----------------|--------| 106 | | Quick Sort | O(n log n) | O(n log n) | O(n²) | O(log n) | No | 107 | | Merge Sort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes | 108 | | Bubble Sort | O(n) | O(n²) | O(n²) | O(1) | Yes | 109 | | Insertion Sort | O(n) | O(n²) | O(n²) | O(1) | Yes | 110 | | Selection Sort | O(n²) | O(n²) | O(n²) | O(1) | No | 111 | | Heap Sort | O(n log n) | O(n log n) | O(n log n) | O(1) | No | 112 | | Counting Sort | O(n+k) | O(n+k) | O(n+k) | O(k) | Yes | 113 | | Radix Sort | O(nk) | O(nk) | O(nk) | O(n+k) | Yes | 114 | 115 | Where: 116 | - n is the number of elements 117 | - k is the range of the input 118 | 119 | ## When to Use Each Algorithm 120 | 121 | - **Quick Sort**: General-purpose sorting, works well for arrays that fit in memory 122 | - **Merge Sort**: When stability is needed and O(n log n) worst-case is required 123 | - **Bubble Sort**: Educational purposes or very small datasets 124 | - **Insertion Sort**: Small datasets or nearly sorted arrays 125 | - **Heap Sort**: When consistent performance is needed without worst-case scenarios 126 | - **Counting/Radix Sort**: When the range of input values is limited 127 | 128 | ## Conclusion 129 | 130 | Choosing the right sorting algorithm depends on your specific requirements, including the size of the dataset, memory constraints, and whether stability is important. In practice, most programming languages implement hybrid sorting algorithms that combine the benefits of different approaches. 131 | -------------------------------------------------------------------------------- /docs/databases/relational.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Tech Notes Hub 3 | tags: 'learning, technology, programming' 4 | update: '2025-06-06' 5 | date: '2025-06-06' 6 | title: Relational 7 | description: Guide about Relational 8 | --- 9 | # Relational Databases 10 | 11 | Relational databases are organized collections of data that store information in tables with rows and columns. They follow the relational model proposed by Edgar F. Codd in 1970, which emphasizes relationships between data entities. 12 | 13 | ## Core Concepts 14 | 15 | ### Tables (Relations) 16 | 17 | The fundamental structure in relational databases: 18 | - Each **table** represents an entity type (e.g., customers, products) 19 | - Each **row** (tuple) represents an instance of that entity 20 | - Each **column** (attribute) represents a property of that entity 21 | 22 | ### Keys 23 | 24 | Keys establish relationships and ensure data integrity: 25 | 26 | - **Primary Key**: Uniquely identifies each record in a table 27 | - **Foreign Key**: References a primary key in another table, establishing relationships 28 | - **Composite Key**: Combines multiple columns to form a unique identifier 29 | - **Candidate Key**: A column or set of columns that could serve as a primary key 30 | 31 | ### Schema 32 | 33 | A schema defines the structure of the database: 34 | - Table definitions 35 | - Column data types and constraints 36 | - Relationships between tables 37 | 38 | ## SQL (Structured Query Language) 39 | 40 | SQL is the standard language for interacting with relational databases. 41 | 42 | ### Basic SQL Commands 43 | 44 | ```sql 45 | -- Create a table 46 | CREATE TABLE customers ( 47 | customer_id INT PRIMARY KEY, 48 | name VARCHAR(100), 49 | email VARCHAR(100), 50 | signup_date DATE 51 | ); 52 | 53 | -- Insert data 54 | INSERT INTO customers (customer_id, name, email, signup_date) 55 | VALUES (1, 'John Smith', 'john@example.com', '2023-01-15'); 56 | 57 | -- Query data 58 | SELECT * FROM customers WHERE signup_date > '2023-01-01'; 59 | 60 | -- Update data 61 | UPDATE customers SET email = 'john.smith@example.com' WHERE customer_id = 1; 62 | 63 | -- Delete data 64 | DELETE FROM customers WHERE customer_id = 1; 65 | ``` 66 | 67 | ## Normalization 68 | 69 | Normalization is the process of organizing data to reduce redundancy and improve data integrity: 70 | 71 | - **First Normal Form (1NF)**: Eliminate duplicate columns and create separate tables for related data 72 | - **Second Normal Form (2NF)**: Meet 1NF requirements and remove partial dependencies 73 | - **Third Normal Form (3NF)**: Meet 2NF requirements and remove transitive dependencies 74 | 75 | ## ACID Properties 76 | 77 | Transactions in relational databases follow ACID properties: 78 | 79 | - **Atomicity**: Transactions are all-or-nothing operations 80 | - **Consistency**: Transactions bring the database from one valid state to another 81 | - **Isolation**: Concurrent transactions don't interfere with each other 82 | - **Durability**: Completed transactions persist even in case of system failure 83 | 84 | ## Popular Relational Database Systems 85 | 86 | - **MySQL**: Open-source, widely used for web applications 87 | - **PostgreSQL**: Advanced open-source database with extensive features 88 | - **Oracle Database**: Enterprise-level commercial database 89 | - **Microsoft SQL Server**: Microsoft's commercial database solution 90 | - **SQLite**: Lightweight, serverless database engine 91 | 92 | ## Indexes 93 | 94 | Indexes speed up data retrieval operations: 95 | - Similar to a book index 96 | - Improves query performance but adds overhead for write operations 97 | - Types include B-tree, hash, and bitmap indexes 98 | 99 | ## Joins 100 | 101 | Joins combine records from two or more tables: 102 | - **INNER JOIN**: Returns records with matching values in both tables 103 | - **LEFT JOIN**: Returns all records from the left table and matching records from the right 104 | - **RIGHT JOIN**: Returns all records from the right table and matching records from the left 105 | - **FULL JOIN**: Returns all records when there's a match in either table 106 | 107 | ```sql 108 | SELECT customers.name, orders.order_date 109 | FROM customers 110 | INNER JOIN orders ON customers.customer_id = orders.customer_id; 111 | ``` 112 | 113 | ## When to Use Relational Databases 114 | 115 | Relational databases are ideal for: 116 | - Structured data with clear relationships 117 | - Applications requiring complex queries and transactions 118 | - Systems where data integrity is critical 119 | - Scenarios where consistency is more important than speed 120 | 121 | ## References 122 | 123 | - Codd, E.F. (1970). "A Relational Model of Data for Large Shared Data Banks" 124 | - Date, C.J. "An Introduction to Database Systems" 125 | - Garcia-Molina, H., Ullman, J.D., & Widom, J. "Database Systems: The Complete Book" 126 | -------------------------------------------------------------------------------- /i18n/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/i18n/init.txt -------------------------------------------------------------------------------- /i18n/vi/README.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub 2 | 3 | Chào mừng đến với Tech Notes Hub! Đây là kho lưu trữ toàn diện các ghi chú kỹ thuật, đoạn mã và ví dụ bao gồm nhiều chủ đề trong phát triển phần mềm, thiết kế hệ thống và khoa học máy tính. 4 | 5 | ## Mục đích 6 | 7 | Tech Notes Hub nhằm mục đích: 8 | 9 | - Cung cấp các giải thích rõ ràng, ngắn gọn về các khái niệm kỹ thuật quan trọng 10 | - Cung cấp các ví dụ mã thực tế bằng nhiều ngôn ngữ lập trình 11 | - Phục vụ như một tài liệu tham khảo cho các nhà phát triển ở mọi cấp độ kỹ năng 12 | - Tạo ra một cơ sở kiến thức hợp tác cho cộng đồng công nghệ 13 | 14 | ## Cấu trúc kho lưu trữ 15 | 16 | Kho lưu trữ được tổ chức thành các phần chính sau: 17 | 18 | ### Algorithms (Thuật toán) 19 | Cài đặt và giải thích về các thuật toán phổ biến, bao gồm tìm kiếm, sắp xếp, duyệt đồ thị, và nhiều hơn nữa. 20 | 21 | ### Databases (Cơ sở dữ liệu) 22 | Ghi chú về hệ thống cơ sở dữ liệu, tối ưu hóa truy vấn, mô hình hóa dữ liệu và các phương pháp tốt nhất cho cả cơ sở dữ liệu SQL và NoSQL. 23 | 24 | ### Design Patterns (Mẫu thiết kế) 25 | Giải thích chi tiết và cài đặt các mẫu thiết kế phần mềm trên nhiều ngôn ngữ lập trình. 26 | 27 | ### DevOps 28 | Thông tin về tích hợp liên tục, triển khai, container hóa và quản lý cơ sở hạ tầng. 29 | 30 | ### Linux 31 | Hướng dẫn về quản trị hệ thống Linux, lập trình shell và các công cụ dòng lệnh. 32 | 33 | ### System Design (Thiết kế hệ thống) 34 | Các cách tiếp cận để thiết kế các hệ thống phần mềm có khả năng mở rộng, đáng tin cậy và dễ bảo trì. 35 | 36 | ### Testing (Kiểm thử) 37 | Các phương pháp tốt nhất cho kiểm thử đơn vị, kiểm thử tích hợp và phát triển hướng kiểm thử. 38 | 39 | ## Cách sử dụng kho lưu trữ này 40 | 41 | Mỗi phần chứa các tệp markdown với giải thích và đoạn mã. Bạn có thể: 42 | 43 | 1. Duyệt qua các phần để tìm chủ đề quan tâm 44 | 2. Sử dụng các ví dụ mã làm tham khảo cho dự án của riêng bạn 45 | 3. Đóng góp bằng cách thêm nội dung mới hoặc cải thiện tài liệu hiện có 46 | 47 | ## Đóng góp 48 | 49 | Chúng tôi hoan nghênh đóng góp từ cộng đồng! Vui lòng xem tệp [CONTRIBUTING_vi.md](../CONTRIBUTING_vi.md) của chúng tôi để biết hướng dẫn về cách đóng góp. 50 | 51 | ## Giấy phép 52 | 53 | Dự án này được cấp phép theo Giấy phép MIT - xem tệp [LICENSE.txt](../LICENSE.txt) để biết chi tiết. 54 | -------------------------------------------------------------------------------- /i18n/vi/algorithms/sorting-algorithms.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Tech Notes Hub 3 | tags: 'learning, technology, programming' 4 | update: '2025-06-06' 5 | date: '2025-06-06' 6 | title: Sorting Algorithms 7 | description: Guide about Sorting Algorithms 8 | --- 9 | # Thuật toán sắp xếp 10 | 11 | Sắp xếp là một trong những hoạt động cơ bản nhất trong khoa học máy tính. Bài viết này khám phá các thuật toán sắp xếp khác nhau, cách triển khai và đặc điểm hiệu suất của chúng. 12 | 13 | ## Quick Sort (Sắp xếp nhanh) 14 | 15 | Quick Sort là một thuật toán chia để trị hoạt động bằng cách chọn một phần tử 'trục' từ mảng và phân vùng các phần tử khác thành hai mảng con theo điều kiện chúng nhỏ hơn hoặc lớn hơn trục. 16 | 17 | ```python 18 | def quick_sort(arr): 19 | if len(arr) <= 1: 20 | return arr 21 | pivot = arr[len(arr) // 2] 22 | left = [x for x in arr if x < pivot] 23 | middle = [x for x in arr if x == pivot] 24 | right = [x for x in arr if x > pivot] 25 | return quick_sort(left) + middle + quick_sort(right) 26 | 27 | # Ví dụ sử dụng 28 | arr = [3, 6, 8, 10, 1, 2, 1] 29 | print(quick_sort(arr)) # Kết quả: [1, 1, 2, 3, 6, 8, 10] 30 | ``` 31 | 32 | **Độ phức tạp thời gian**: 33 | - Trường hợp tốt nhất: O(n log n) 34 | - Trường hợp trung bình: O(n log n) 35 | - Trường hợp xấu nhất: O(n²) 36 | 37 | ## Merge Sort (Sắp xếp trộn) 38 | 39 | Merge Sort là một thuật toán chia để trị khác, chia mảng đầu vào thành hai nửa, sắp xếp đệ quy chúng, và sau đó trộn các nửa đã sắp xếp. 40 | 41 | ```python 42 | def merge_sort(arr): 43 | if len(arr) <= 1: 44 | return arr 45 | 46 | mid = len(arr) // 2 47 | left = merge_sort(arr[:mid]) 48 | right = merge_sort(arr[mid:]) 49 | 50 | return merge(left, right) 51 | 52 | def merge(left, right): 53 | result = [] 54 | i = j = 0 55 | 56 | while i < len(left) and j < len(right): 57 | if left[i] < right[j]: 58 | result.append(left[i]) 59 | i += 1 60 | else: 61 | result.append(right[j]) 62 | j += 1 63 | 64 | result.extend(left[i:]) 65 | result.extend(right[j:]) 66 | return result 67 | ``` 68 | 69 | **Độ phức tạp thời gian**: 70 | - Trường hợp tốt nhất: O(n log n) 71 | - Trường hợp trung bình: O(n log n) 72 | - Trường hợp xấu nhất: O(n log n) 73 | 74 | ## Bubble Sort (Sắp xếp nổi bọt) 75 | 76 | Bubble Sort là một thuật toán đơn giản dựa trên so sánh, lặp đi lặp lại qua danh sách, so sánh các phần tử liền kề và hoán đổi chúng nếu chúng không đúng thứ tự. 77 | 78 | ```python 79 | def bubble_sort(arr): 80 | n = len(arr) 81 | for i in range(n): 82 | # Cờ để tối ưu hóa nếu mảng đã được sắp xếp 83 | swapped = False 84 | 85 | for j in range(0, n - i - 1): 86 | if arr[j] > arr[j + 1]: 87 | arr[j], arr[j + 1] = arr[j + 1], arr[j] 88 | swapped = True 89 | 90 | # Nếu không có hoán đổi nào xảy ra trong lần này, mảng đã được sắp xếp 91 | if not swapped: 92 | break 93 | 94 | return arr 95 | ``` 96 | 97 | **Độ phức tạp thời gian**: 98 | - Trường hợp tốt nhất: O(n) - khi mảng đã được sắp xếp 99 | - Trường hợp trung bình: O(n²) 100 | - Trường hợp xấu nhất: O(n²) 101 | 102 | ## So sánh các thuật toán sắp xếp 103 | 104 | | Thuật toán | Độ phức tạp (Tốt nhất) | Độ phức tạp (Trung bình) | Độ phức tạp (Xấu nhất) | Độ phức tạp không gian | Ổn định | 105 | |------------|------------------------|--------------------------|------------------------|------------------------|---------| 106 | | Quick Sort | O(n log n) | O(n log n) | O(n²) | O(log n) | Không | 107 | | Merge Sort | O(n log n) | O(n log n) | O(n log n) | O(n) | Có | 108 | | Bubble Sort | O(n) | O(n²) | O(n²) | O(1) | Có | 109 | | Insertion Sort | O(n) | O(n²) | O(n²) | O(1) | Có | 110 | | Selection Sort | O(n²) | O(n²) | O(n²) | O(1) | Không | 111 | | Heap Sort | O(n log n) | O(n log n) | O(n log n) | O(1) | Không | 112 | | Counting Sort | O(n+k) | O(n+k) | O(n+k) | O(k) | Có | 113 | | Radix Sort | O(nk) | O(nk) | O(nk) | O(n+k) | Có | 114 | 115 | Trong đó: 116 | - n là số lượng phần tử 117 | - k là phạm vi của giá trị đầu vào 118 | 119 | ## Khi nào sử dụng từng thuật toán 120 | 121 | - **Quick Sort**: Sắp xếp đa năng, hoạt động tốt cho các mảng vừa với bộ nhớ 122 | - **Merge Sort**: Khi cần tính ổn định và yêu cầu trường hợp xấu nhất O(n log n) 123 | - **Bubble Sort**: Mục đích giáo dục hoặc tập dữ liệu rất nhỏ 124 | - **Insertion Sort**: Tập dữ liệu nhỏ hoặc mảng gần như đã được sắp xếp 125 | - **Heap Sort**: Khi cần hiệu suất ổn định mà không có kịch bản xấu nhất 126 | - **Counting/Radix Sort**: Khi phạm vi giá trị đầu vào bị giới hạn 127 | 128 | ## Kết luận 129 | 130 | Việc chọn thuật toán sắp xếp phù hợp phụ thuộc vào yêu cầu cụ thể của bạn, bao gồm kích thước của tập dữ liệu, giới hạn bộ nhớ và liệu tính ổn định có quan trọng hay không. Trong thực tế, hầu hết các ngôn ngữ lập trình triển khai các thuật toán sắp xếp kết hợp kết hợp lợi ích của các phương pháp khác nhau. 131 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "glob": "^11.0.2", 4 | "gray-matter": "^4.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /snippets/algorithms/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/snippets/algorithms/init.txt -------------------------------------------------------------------------------- /snippets/databases/relational/.env.example: -------------------------------------------------------------------------------- 1 | # Database configuration example file 2 | # Copy this file to .env and fill in your actual values 3 | 4 | # SQLite Configuration 5 | SQLITE_DB_PATH=:memory: 6 | 7 | # PostgreSQL Configuration 8 | PG_HOST=localhost 9 | PG_DATABASE=mydb 10 | PG_USER=postgres 11 | PG_PASSWORD=your_secure_password 12 | 13 | # MySQL Configuration 14 | MYSQL_HOST=localhost 15 | MYSQL_DATABASE=mydb 16 | MYSQL_USER=root 17 | MYSQL_PASSWORD=your_secure_password 18 | 19 | # SQL Server Configuration 20 | SQLSERVER_CONNECTION_STRING=Server=localhost;Database=TestDB;User Id=sa;Password=your_secure_password; 21 | 22 | # Other database connection strings 23 | POSTGRES_CONNECTION_STRING=Host=localhost;Database=testdb;Username=postgres;Password=your_secure_password; 24 | MYSQL_CONNECTION_STRING=Server=localhost;Database=testdb;Uid=root;Pwd=your_secure_password; 25 | DAPPER_CONNECTION_STRING=Data Source=:memory: 26 | 27 | # SQLAlchemy URL for Python ORM 28 | # Format examples: 29 | # SQLite: sqlite:///path/to/database.db 30 | # PostgreSQL: postgresql://user:password@localhost/dbname 31 | # MySQL: mysql://user:password@localhost/dbname 32 | SQLALCHEMY_DATABASE_URL=sqlite:///:memory: 33 | -------------------------------------------------------------------------------- /snippets/devops/ci-cd/ci-cd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Simple CI/CD Pipeline Script 4 | # This script demonstrates a basic CI/CD pipeline for a Node.js application 5 | # 6 | 7 | # Exit immediately if a command exits with a non-zero status 8 | set -e 9 | 10 | # Print commands and their arguments as they are executed 11 | set -x 12 | 13 | # Define variables 14 | APP_NAME="my-nodejs-app" 15 | REPO_URL="https://github.com/username/my-nodejs-app.git" 16 | DEPLOY_DIR="/var/www/my-nodejs-app" 17 | LOG_FILE="pipeline.log" 18 | 19 | # Function to log messages 20 | log_message() { 21 | local timestamp=$(date +"%Y-%m-%d %H:%M:%S") 22 | echo "[$timestamp] $1" | tee -a $LOG_FILE 23 | } 24 | 25 | # Function to handle errors 26 | handle_error() { 27 | log_message "ERROR: An error occurred on line $1" 28 | exit 1 29 | } 30 | 31 | # Set up error handling 32 | trap 'handle_error $LINENO' ERR 33 | 34 | # Start the pipeline 35 | log_message "Starting CI/CD pipeline for $APP_NAME" 36 | 37 | # Step 1: Clean workspace 38 | log_message "Step 1: Cleaning workspace" 39 | rm -rf build 40 | mkdir -p build 41 | cd build 42 | 43 | # Step 2: Clone the repository 44 | log_message "Step 2: Cloning repository" 45 | git clone $REPO_URL . 46 | git checkout main 47 | 48 | # Step 3: Install dependencies 49 | log_message "Step 3: Installing dependencies" 50 | npm ci 51 | 52 | # Step 4: Run linting 53 | log_message "Step 4: Running linting" 54 | npm run lint 55 | 56 | # Step 5: Run tests 57 | log_message "Step 5: Running tests" 58 | npm test 59 | 60 | # Step 6: Build the application 61 | log_message "Step 6: Building the application" 62 | npm run build 63 | 64 | # Step 7: Run security audit 65 | log_message "Step 7: Running security audit" 66 | npm audit --production 67 | 68 | # Step 8: Deploy to production 69 | log_message "Step 8: Deploying to production" 70 | # Check if deploy directory exists 71 | if [ ! -d "$DEPLOY_DIR" ]; then 72 | mkdir -p $DEPLOY_DIR 73 | fi 74 | 75 | # Copy build files to deploy directory 76 | cp -r dist/* $DEPLOY_DIR 77 | 78 | # Step 9: Restart the application 79 | log_message "Step 9: Restarting the application" 80 | # This is a simplified example - in reality, you might use systemd, PM2, etc. 81 | cd $DEPLOY_DIR 82 | npm run stop || true # Don't fail if the app isn't running 83 | npm run start 84 | 85 | # Step 10: Run smoke tests 86 | log_message "Step 10: Running smoke tests" 87 | # Simple curl test to verify the application is responding 88 | sleep 5 # Wait for the app to start 89 | if curl -s http://localhost:3000/health | grep -q "ok"; then 90 | log_message "Smoke test passed!" 91 | else 92 | log_message "Smoke test failed!" 93 | exit 1 94 | fi 95 | 96 | # Pipeline completed successfully 97 | log_message "CI/CD pipeline completed successfully!" 98 | -------------------------------------------------------------------------------- /snippets/devops/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/snippets/devops/init.txt -------------------------------------------------------------------------------- /snippets/linux/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/snippets/linux/init.txt -------------------------------------------------------------------------------- /snippets/system-design/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/snippets/system-design/init.txt -------------------------------------------------------------------------------- /snippets/testing/init.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/snippets/testing/init.txt -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools for Tech Notes Hub 2 | 3 | This directory contains utility scripts for maintaining the Tech Notes Hub project. 4 | 5 | ## Scripts 6 | 7 | ### `update-frontmatter.js` 8 | 9 | A Node.js script that automatically updates the frontmatter (metadata) in all Markdown files in the `/docs` and `/i18n/vi` directories. 10 | 11 | #### Features 12 | 13 | - Ensures all Markdown files have consistent frontmatter 14 | - Updates the `update` field with the current date **only if the field doesn't exists** 15 | - Sets default values for missing fields: 16 | - `author`: "Tech Notes Hub" 17 | - `date`: Current date (if missing) 18 | - `tags`: Default tags (if missing) 19 | - `title`: Generated from filename (if missing) 20 | - `description`: Generated from title (if missing) 21 | - Logs include timestamps for better tracking 22 | 23 | #### Requirements 24 | 25 | - Node.js 26 | - Dependencies: 27 | - `gray-matter` 28 | - `glob` 29 | 30 | #### Usage 31 | 32 | **Update all files:** 33 | 34 | ```bash 35 | # Install dependencies if not already installed 36 | npm install 37 | 38 | # Make the script executable 39 | chmod +x tools/update-frontmatter.js 40 | 41 | # Run the script to update all files 42 | node tools/update-frontmatter.js 43 | # Or explicitly specify all files 44 | node tools/update-frontmatter.js --all 45 | ``` 46 | 47 | **Update a single file manually:** 48 | 49 | ```bash 50 | # Update a specific file by providing the relative path from either docs/ or i18n/vi/ 51 | node tools/update-frontmatter.js algorithms/sorting-algorithms.md 52 | 53 | # The script will check for the file in both directories: 54 | # - /docs/algorithms/sorting-algorithms.md 55 | # - /i18n/vi/algorithms/sorting-algorithms.md 56 | ``` 57 | 58 | ### `generate_summary.py` 59 | 60 | A Python script that automatically generates the `SUMMARY.md` file by scanning the `docs/` directory structure. 61 | 62 | #### Features 63 | 64 | - Creates a well-structured table of contents 65 | - Extracts titles from Markdown files when available 66 | - Organizes content by categories based on directory structure 67 | - Generates clean, formatted links to all documentation files 68 | 69 | #### Requirements 70 | 71 | - Python 3.6+ 72 | - No external dependencies (uses only standard library) 73 | 74 | #### Usage 75 | 76 | ```bash 77 | # Make the script executable (first time only) 78 | chmod +x tools/generate_summary.py 79 | 80 | # Run the script to generate SUMMARY.md 81 | python tools/generate_summary.py 82 | ``` 83 | 84 | The script will: 85 | 1. Scan all directories in `/docs` 86 | 2. Extract titles from Markdown files or generate them from filenames 87 | 3. Create a hierarchical table of contents 88 | 4. Write the result to `SUMMARY.md` in the project root 89 | 90 | #### Automation 91 | 92 | This script is configured to run automatically via GitHub Actions when: 93 | 94 | 1. Any Markdown file is changed in the `/docs` or `/i18n` directories 95 | 2. Weekly on Monday at 00:00 UTC 96 | 3. Manually via the GitHub Actions interface 97 | 98 | The automation will commit any changes back to the repository with a commit message "chore: update markdown frontmatter [skip ci]". 99 | 100 | ## Adding New Tools 101 | 102 | When adding new tools to this directory, please follow these guidelines: 103 | 104 | 1. Create a well-documented script with clear comments 105 | 2. Update this README with information about the tool 106 | 3. Ensure the tool follows project coding standards 107 | 4. Add any necessary dependencies to the project's package.json 108 | -------------------------------------------------------------------------------- /tools/check_links.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Link Checker Script 4 | # 5 | # This script checks for broken links in markdown files within the repository. 6 | # It uses markdown-link-check to validate both internal and external links. 7 | # 8 | # Usage: 9 | # ./check_links.sh [directory] 10 | # 11 | # If no directory is specified, it will check all markdown files in the repo. 12 | # 13 | # Requirements: 14 | # - npm (Node Package Manager) 15 | # - markdown-link-check (will be installed if not present) 16 | # 17 | 18 | set -e 19 | 20 | # Default search directory is the repo root 21 | SEARCH_DIR=${1:-$(git rev-parse --show-toplevel)} 22 | 23 | # Colors for output 24 | RED='\033[0;31m' 25 | GREEN='\033[0;32m' 26 | YELLOW='\033[0;33m' 27 | NC='\033[0m' # No Color 28 | 29 | echo -e "${YELLOW}=== Tech Notes Hub Link Checker ===${NC}" 30 | 31 | # Check if markdown-link-check is installed, if not install it 32 | if ! command -v markdown-link-check &> /dev/null; then 33 | echo -e "${YELLOW}markdown-link-check not found. Installing...${NC}" 34 | npm install -g markdown-link-check 35 | fi 36 | 37 | # Configuration file for markdown-link-check 38 | CONFIG_FILE=$(mktemp) 39 | cat > "$CONFIG_FILE" << EOF 40 | { 41 | "ignorePatterns": [ 42 | { 43 | "pattern": "^#" 44 | }, 45 | { 46 | "pattern": "^mailto:" 47 | } 48 | ], 49 | "replacementPatterns": [ 50 | { 51 | "pattern": "^/", 52 | "replacement": "file://$(pwd)/" 53 | } 54 | ], 55 | "timeout": "5s", 56 | "retryOn429": true, 57 | "retryCount": 3, 58 | "fallbackRetryDelay": "30s" 59 | } 60 | EOF 61 | 62 | # Find all markdown files in the specified directory 63 | echo -e "${YELLOW}Searching for markdown files in ${SEARCH_DIR}...${NC}" 64 | FILES=$(find "$SEARCH_DIR" -name "*.md" | sort) 65 | 66 | if [ -z "$FILES" ]; then 67 | echo -e "${RED}No markdown files found in ${SEARCH_DIR}${NC}" 68 | rm "$CONFIG_FILE" 69 | exit 1 70 | fi 71 | 72 | TOTAL_FILES=$(echo "$FILES" | wc -l) 73 | TOTAL_FILES=$(echo "$TOTAL_FILES" | tr -d '[:space:]') 74 | echo -e "${GREEN}Found ${TOTAL_FILES} markdown files to check${NC}" 75 | 76 | # Variables to track results 77 | PASSED=0 78 | FAILED=0 79 | FAILED_FILES="" 80 | 81 | # Process each file 82 | for FILE in $FILES; do 83 | echo -e "${YELLOW}Checking links in ${FILE}...${NC}" 84 | 85 | # Run markdown-link-check 86 | if markdown-link-check --config "$CONFIG_FILE" "$FILE" | grep -q "ERROR"; then 87 | echo -e "${RED}❌ Failed: ${FILE}${NC}" 88 | FAILED=$((FAILED+1)) 89 | FAILED_FILES="${FAILED_FILES}\n${FILE}" 90 | else 91 | echo -e "${GREEN}✅ Passed: ${FILE}${NC}" 92 | PASSED=$((PASSED+1)) 93 | fi 94 | done 95 | 96 | # Clean up 97 | rm "$CONFIG_FILE" 98 | 99 | # Print summary 100 | echo -e "\n${YELLOW}=== Summary ===${NC}" 101 | echo -e "${GREEN}✅ ${PASSED} files passed${NC}" 102 | if [ $FAILED -gt 0 ]; then 103 | echo -e "${RED}❌ ${FAILED} files failed:${FAILED_FILES}${NC}" 104 | exit 1 105 | else 106 | echo -e "${GREEN}All links are valid!${NC}" 107 | exit 0 108 | fi -------------------------------------------------------------------------------- /tools/generate_summary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Script to automatically generate SUMMARY.md by scanning the docs/ directory. 4 | """ 5 | 6 | import os 7 | import re 8 | from pathlib import Path 9 | 10 | def generate_summary(): 11 | """Generate summary based on docs structure""" 12 | docs_dir = Path("docs") 13 | summary_path = Path("SUMMARY.md") 14 | 15 | # Start with header 16 | summary_content = "# Tech Notes Hub\n\n" 17 | summary_content += "## Table of Contents\n\n" 18 | 19 | # Process each directory in docs/ 20 | for path in sorted(docs_dir.glob("*")): 21 | if path.is_dir(): 22 | dir_name = path.name 23 | pretty_dir_name = dir_name.replace('-', ' ').title() 24 | summary_content += f"### {pretty_dir_name}\n\n" 25 | 26 | # Process files in the directory 27 | for file in sorted(path.glob("*.md")): 28 | file_name = file.stem 29 | 30 | # Skip files that start with underscore (like _category_.json files) 31 | if file_name.startswith('_'): 32 | continue 33 | 34 | # Get the title from the file's first heading 35 | title = get_title_from_file(file) 36 | if not title: 37 | title = file_name.replace('-', ' ').title() 38 | 39 | relative_path = os.path.join(path.name, file.name) 40 | summary_content += f"- [{title}](docs/{relative_path})\n" 41 | 42 | summary_content += "\n" 43 | 44 | # Write the summary file 45 | with open(summary_path, "w", encoding="utf-8") as f: 46 | f.write(summary_content) 47 | 48 | print(f"Generated {summary_path}") 49 | 50 | def get_title_from_file(file_path): 51 | """Extract the title from the first heading in the file""" 52 | try: 53 | with open(file_path, "r", encoding="utf-8") as f: 54 | content = f.read() 55 | # Look for first # heading 56 | match = re.search(r'^# (.+)$', content, re.MULTILINE) 57 | if match: 58 | return match.group(1) 59 | except Exception as e: 60 | print(f"Warning: Could not extract title from {file_path}: {e}") 61 | 62 | return None 63 | 64 | if __name__ == "__main__": 65 | generate_summary() -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub Website 2 | 3 | A modern blog website built with Next.js, Tailwind CSS, shadcn UI, and i18n support for multilingual content (Vietnamese and English). 4 | 5 | ## Features 6 | 7 | - **Modern UI**: Clean, minimal design with Tailwind CSS and shadcn UI components 8 | - **Bilingual Support**: Full i18n integration with Vietnamese and English translations 9 | - **Blog System**: Complete blog functionality with Markdown content rendering 10 | - **Dark/Light Mode**: Theme switching with system preference detection 11 | - **SEO Optimized**: Complete SEO setup with meta tags, OpenGraph data, dynamic sitemap.xml, robots.txt, structured metadata, canonical URLs and language alternates 12 | - **Responsive Design**: Mobile-first approach, works on all devices 13 | 14 | ## Tech Stack 15 | 16 | - [Next.js 14](https://nextjs.org/) - React framework 17 | - [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework 18 | - [shadcn UI](https://ui.shadcn.com/) - UI component library 19 | - [React Markdown](https://github.com/remarkjs/react-markdown) - Markdown renderer 20 | - [i18n](https://nextjs.org/docs/app/building-your-application/routing/internationalization) - Internationalization 21 | - [next-themes](https://github.com/pacocoursey/next-themes) - Theme management 22 | 23 | ## Getting Started 24 | 25 | ### Prerequisites 26 | 27 | - Node.js 18.17 or later 28 | - npm or yarn 29 | 30 | ### Installation 31 | 32 | 1. Clone the repository 33 | ```bash 34 | git clone 35 | cd website 36 | ``` 37 | 38 | 2. Install dependencies 39 | ```bash 40 | npm install 41 | # or 42 | yarn install 43 | ``` 44 | 45 | 3. Run the development server 46 | ```bash 47 | npm run dev 48 | # or 49 | yarn dev 50 | ``` 51 | 52 | 4. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result 53 | 54 | ## Project Structure 55 | 56 | ``` 57 | website/ 58 | ├── public/ # Static assets 59 | ├── src/ 60 | │ ├── app/ # App router pages 61 | │ │ ├── [locale]/ # Locale-specific routes 62 | │ │ │ ├── blog/ # Blog pages 63 | │ │ │ ├── about/ # About page 64 | │ │ ├── components/ # React components 65 | │ │ │ ├── ui/ # UI components (shadcn) 66 | │ │ │ ├── layout/ # Layout components 67 | │ │ │ ├── blog/ # Blog-specific components 68 | │ │ ├── data/ # Data sources 69 | │ │ │ ├── i18n/ # Translation files 70 | │ │ │ ├── blog-posts.ts # Blog post data 71 | │ │ ├── lib/ # Utility functions 72 | │ │ │ ├── i18n/ # i18n utilities 73 | │ │ │ ├── utils.ts # Helper functions 74 | │ │ │ ├── types.ts # TypeScript types 75 | ├── next.config.ts # Next.js configuration 76 | ├── tailwind.config.js # Tailwind CSS configuration 77 | ``` 78 | 79 | ## Adding Content 80 | 81 | ### Blog Posts 82 | 83 | To add new blog posts, edit the `src/data/blog-posts.ts` file. Each post should follow the BlogPost interface defined in `src/lib/types.ts`. 84 | 85 | ### Translations 86 | 87 | Add or edit translations in the following files: 88 | - `src/data/i18n/en/common.json` - English translations 89 | - `src/data/i18n/vi/common.json` - Vietnamese translations 90 | 91 | ## Deployment 92 | 93 | ### Build for Production 94 | 95 | ```bash 96 | npm run build 97 | # or 98 | yarn build 99 | ``` 100 | 101 | ### Deploy to Vercel 102 | 103 | The easiest way to deploy the application is to use the [Vercel Platform](https://vercel.com/). 104 | 105 | 1. Push your code to a Git repository (GitHub, GitLab, or Bitbucket) 106 | 2. Import the project to Vercel 107 | 3. Vercel will detect Next.js and configure the build settings automatically 108 | 4. Click "Deploy" 109 | 110 | ### Other Deployment Options 111 | 112 | You can also deploy to other platforms like Netlify, AWS Amplify, or traditional hosting with a Node.js server. 113 | 114 | For static export: 115 | 116 | ```bash 117 | npm run build 118 | npm run export 119 | ``` 120 | 121 | This will generate a static version of the site in the `out` directory that can be served by any static hosting service. 122 | 123 | ## License 124 | 125 | This project is licensed under the MIT License - see the LICENSE file for details. 126 | -------------------------------------------------------------------------------- /website/WEBSITE_README.md: -------------------------------------------------------------------------------- 1 | # Tech Notes Hub Blog Website 2 | 3 | This is the blog website component of the Tech Notes Hub project. The website is built using Next.js, Tailwind CSS, shadcn UI, and i18n support for multilingual content (Vietnamese and English). 4 | 5 | ## Overview 6 | 7 | The blog website serves as a frontend for displaying technical content and notes from the Tech Notes Hub repository. It provides a modern, responsive interface for users to browse and read technical articles. 8 | 9 | ## Getting Started 10 | 11 | The blog website is located in the `/website` directory. To start working with it: 12 | 13 | ```bash 14 | cd website 15 | npm install 16 | npm run dev 17 | ``` 18 | 19 | Open [http://localhost:3000](http://localhost:3000) to view the site in your browser. 20 | 21 | ## Features 22 | 23 | - Modern UI built with Next.js and Tailwind CSS 24 | - Bilingual support (Vietnamese and English) 25 | - Dark/Light mode 26 | - Responsive design 27 | - SEO optimized 28 | - Markdown content rendering 29 | 30 | ## Documentation 31 | 32 | For detailed information about the blog website, including setup instructions, project structure, and deployment guidelines, please refer to the [Website README](/website/README.md) in the website directory. 33 | -------------------------------------------------------------------------------- /website/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /website/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /website/next-seo.config.js: -------------------------------------------------------------------------------- 1 | // Default SEO configuration 2 | const defaultSEOConfig = { 3 | titleTemplate: '%s - Tech Notes Hub', 4 | defaultTitle: 'Tech Notes Hub', 5 | description: 'A collection of tech notes, code snippets, and technical guides for developers', 6 | canonical: 'https://tech-notes-hub.vercel.app', 7 | openGraph: { 8 | type: 'website', 9 | locale: 'vi_VN', 10 | url: 'https://tech-notes-hub.vercel.app', 11 | siteName: 'Tech Notes Hub', 12 | title: 'Tech Notes Hub', 13 | description: 'A collection of tech notes, code snippets, and technical guides for developers', 14 | images: [ 15 | { 16 | url: 'https://tech-notes-hub.vercel.app/og-image.jpg', 17 | width: 1200, 18 | height: 630, 19 | alt: 'Tech Notes Hub', 20 | }, 21 | ], 22 | }, 23 | twitter: { 24 | handle: '@technotes', 25 | site: '@technotes', 26 | cardType: 'summary_large_image', 27 | }, 28 | additionalMetaTags: [ 29 | { 30 | name: 'viewport', 31 | content: 'width=device-width, initial-scale=1', 32 | }, 33 | { 34 | name: 'author', 35 | content: 'Tech Notes Team', 36 | }, 37 | { 38 | name: 'keywords', 39 | content: 'tech, programming, algorithms, design patterns, databases, devops, linux, system design, testing', 40 | }, 41 | ], 42 | additionalLinkTags: [ 43 | { 44 | rel: 'icon', 45 | href: '/favicon.ico', 46 | }, 47 | { 48 | rel: 'apple-touch-icon', 49 | href: '/apple-touch-icon.png', 50 | sizes: '180x180', 51 | }, 52 | { 53 | rel: 'manifest', 54 | href: '/site.webmanifest', 55 | }, 56 | ], 57 | }; 58 | 59 | export default defaultSEOConfig; 60 | -------------------------------------------------------------------------------- /website/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | eslint: { 4 | // Disable ESLint checks during build 5 | ignoreDuringBuilds: true, 6 | }, 7 | typescript: { 8 | // Ignore TypeScript errors during build 9 | ignoreBuildErrors: true, 10 | }, 11 | } 12 | 13 | module.exports = nextConfig 14 | -------------------------------------------------------------------------------- /website/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | reactStrictMode: true, 5 | swcMinify: true, 6 | images: { 7 | domains: ['technotes.example.com'], 8 | }, 9 | // Configure experimental features if needed 10 | experimental: { 11 | // Add experimental features here when required 12 | }, 13 | // Add custom headers for security and caching 14 | async headers() { 15 | return [ 16 | { 17 | source: '/(.*)', 18 | headers: [ 19 | { 20 | key: 'X-DNS-Prefetch-Control', 21 | value: 'on', 22 | }, 23 | { 24 | key: 'X-XSS-Protection', 25 | value: '1; mode=block', 26 | }, 27 | { 28 | key: 'X-Content-Type-Options', 29 | value: 'nosniff', 30 | }, 31 | { 32 | key: 'Referrer-Policy', 33 | value: 'strict-origin-when-cross-origin', 34 | }, 35 | ], 36 | }, 37 | { 38 | // Apply caching headers for static assets 39 | source: '/(.*).(jpg|jpeg|png|gif|webp|svg|ico)', 40 | headers: [ 41 | { 42 | key: 'Cache-Control', 43 | value: 'public, max-age=31536000, immutable', 44 | } 45 | ], 46 | }, 47 | ]; 48 | }, 49 | // Redirect from root to default locale 50 | async redirects() { 51 | return [ 52 | { 53 | source: '/', 54 | destination: '/en', 55 | permanent: true, 56 | }, 57 | ]; 58 | }, 59 | }; 60 | 61 | export default nextConfig; 62 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-accordion": "^1.2.11", 13 | "@radix-ui/react-dialog": "^1.1.14", 14 | "@radix-ui/react-dropdown-menu": "^2.1.15", 15 | "@radix-ui/react-label": "^2.1.7", 16 | "@radix-ui/react-navigation-menu": "^1.2.13", 17 | "@radix-ui/react-slot": "^1.2.3", 18 | "@radix-ui/react-toast": "^1.2.14", 19 | "@reduxjs/toolkit": "^2.8.2", 20 | "@tailwindcss/typography": "^0.5.16", 21 | "@types/react-syntax-highlighter": "^15.5.13", 22 | "class-variance-authority": "^0.7.1", 23 | "clsx": "^2.1.1", 24 | "github-slugger": "^2.0.0", 25 | "i18next": "^25.2.1", 26 | "i18next-browser-languagedetector": "^8.1.0", 27 | "lucide-react": "^0.513.0", 28 | "next": "15.3.3", 29 | "next-i18next": "^15.4.2", 30 | "next-themes": "^0.4.6", 31 | "prismjs": "^1.30.0", 32 | "react": "^19.0.0", 33 | "react-dom": "^19.0.0", 34 | "react-i18next": "^15.5.2", 35 | "react-markdown": "^10.1.0", 36 | "react-redux": "^9.2.0", 37 | "react-syntax-highlighter": "^15.6.1", 38 | "rehype-highlight": "^7.0.2", 39 | "rehype-raw": "^7.0.0", 40 | "rehype-sanitize": "^6.0.0", 41 | "remark-gfm": "^4.0.1", 42 | "shadcn-ui": "^0.9.5", 43 | "sonner": "^2.0.5", 44 | "tailwind-merge": "^3.3.0", 45 | "tailwindcss-animate": "^1.0.7" 46 | }, 47 | "devDependencies": { 48 | "@eslint/eslintrc": "^3", 49 | "@tailwindcss/postcss": "^4", 50 | "@types/node": "^20", 51 | "@types/react": "^19", 52 | "@types/react-dom": "^19", 53 | "eslint": "^9", 54 | "eslint-config-next": "15.3.3", 55 | "tailwindcss": "^4", 56 | "tw-animate-css": "^1.3.4", 57 | "typescript": "^5" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /website/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /website/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/og-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/website/public/og-image.jpg -------------------------------------------------------------------------------- /website/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Allow all web crawlers 2 | User-agent: * 3 | Allow: / 4 | 5 | # Sitemap location 6 | Sitemap: https://tech-notes-hub.vercel.app/sitemap.xml 7 | 8 | # Disallow admin paths 9 | User-agent: * 10 | Disallow: /admin/ 11 | Disallow: /api/ 12 | 13 | # Special instructions for specific bots 14 | User-agent: Googlebot 15 | Allow: / 16 | 17 | User-agent: Bingbot 18 | Allow: / 19 | 20 | User-agent: Baiduspider 21 | Disallow: / 22 | 23 | # Crawl-delay suggestion 24 | User-agent: * 25 | Crawl-delay: 10 26 | -------------------------------------------------------------------------------- /website/public/sitemap.txt: -------------------------------------------------------------------------------- 1 | https://tech-notes-hub.vercel.app/en 2 | https://tech-notes-hub.vercel.app/vi 3 | https://tech-notes-hub.vercel.app/en/about 4 | https://tech-notes-hub.vercel.app/vi/about 5 | https://tech-notes-hub.vercel.app/en/blog 6 | https://tech-notes-hub.vercel.app/vi/blog 7 | https://tech-notes-hub.vercel.app/en/privacy 8 | https://tech-notes-hub.vercel.app/vi/privacy 9 | https://tech-notes-hub.vercel.app/en/terms 10 | https://tech-notes-hub.vercel.app/vi/terms 11 | -------------------------------------------------------------------------------- /website/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/src/app/[locale]/about/page.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { Locale, getTranslationsFromNamespaces } from "@/lib/i18n/settings"; 3 | 4 | type Params = Promise<{ locale: Locale }>; 5 | 6 | export async function generateMetadata({ params }: { params: Params }): Promise { 7 | const { locale } = await params; 8 | const translations = await getTranslationsFromNamespaces(locale, ['common', 'about']); 9 | const about = translations.about; 10 | 11 | return { 12 | title: `${about.title} - ${translations.common.site.title}`, 13 | description: translations.common.site.description, 14 | }; 15 | } 16 | 17 | export default async function AboutPage({ params }: { params: Params }) { 18 | const { locale } = await params; 19 | const translations = await getTranslationsFromNamespaces(locale, ['common', 'about']); 20 | const t = translations.common; 21 | const about = translations.about; 22 | 23 | return ( 24 |
25 |
26 |

27 | {about.title} 28 |

29 | 30 |
31 |

32 | {about.intro} 33 |

34 | 35 |

{about.mission.title}

36 |

{about.mission.content}

37 | 38 |

{about.content.title}

39 |

{about.content.intro}

40 |
    41 | {Object.entries(t.categories).map(([key, value]) => ( 42 |
  • {value}
  • 43 | ))} 44 |
45 | 46 |

{about.community.title}

47 |

{about.community.content}

48 | 49 |

{about.contact.title}

50 |

51 | {about.contact.content}{' '} 52 | 56 | {about.contact.email} 57 | 58 | . 59 |

60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /website/src/app/[locale]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { defaultLocale, getTranslationsFromNamespaces, Locale, locales } from "@/lib/i18n/settings"; 3 | import Header from "@/components/layout/header"; 4 | import { Footer } from "@/components/layout/footer"; 5 | 6 | export async function generateMetadata({ params }: { params: { locale: string } }): Promise { 7 | const locale = params.locale as Locale || defaultLocale; 8 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 9 | 10 | // Access the description directly from translations object as unknown type 11 | const commonTranslations = translations.common as any; 12 | const description = commonTranslations?.meta?.description || 13 | commonTranslations?.site?.description || 14 | 'A collection of tech notes and snippets'; 15 | 16 | return { 17 | title: { 18 | template: '%s - Tech Notes Hub', 19 | default: 'Tech Notes Hub', 20 | }, 21 | description: description, 22 | icons: { 23 | icon: '/favicon.ico', 24 | apple: '/apple-touch-icon.png', 25 | }, 26 | robots: { 27 | index: true, 28 | follow: true, 29 | googleBot: { 30 | index: true, 31 | follow: true, 32 | 'max-video-preview': -1, 33 | 'max-image-preview': 'large', 34 | 'max-snippet': -1, 35 | }, 36 | }, 37 | openGraph: { 38 | title: 'Tech Notes Hub', 39 | description: description, 40 | url: `https://tech-notes-hub.vercel.app/${locale}`, 41 | siteName: 'Tech Notes Hub', 42 | locale: locale, 43 | type: 'website', 44 | images: [ 45 | { 46 | url: 'https://tech-notes-hub.vercel.app/og-image.jpg', 47 | width: 1200, 48 | height: 630, 49 | alt: 'Tech Notes Hub', 50 | }, 51 | ], 52 | }, 53 | twitter: { 54 | card: 'summary_large_image', 55 | title: 'Tech Notes Hub', 56 | description: description, 57 | creator: '@technotes', 58 | }, 59 | alternates: { 60 | canonical: `https://tech-notes-hub.vercel.app/${locale}`, 61 | languages: { 62 | en: 'https://tech-notes-hub.vercel.app/en', 63 | vi: 'https://tech-notes-hub.vercel.app/vi', 64 | }, 65 | }, 66 | }; 67 | } 68 | 69 | export function generateStaticParams() { 70 | return locales.map((locale) => ({ locale })); 71 | } 72 | 73 | interface RootLayoutProps { 74 | children: React.ReactNode; 75 | params: { 76 | locale: string; 77 | }; 78 | } 79 | 80 | export default async function RootLayout({ 81 | children, 82 | params, 83 | }: RootLayoutProps) { 84 | // Make sure params.locale is set to default if it doesn't exist 85 | const locale = (params?.locale as Locale) || defaultLocale; 86 | 87 | // Ensure getTranslationsFromNamespaces is awaited 88 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 89 | 90 | return ( 91 |
92 |
93 |
94 | {children} 95 |
96 |
97 |
98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /website/src/app/[locale]/privacy/page.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { Locale, getTranslationsFromNamespaces } from "@/lib/i18n/settings"; 3 | 4 | type Params = Promise<{ locale: Locale }>; 5 | 6 | export async function generateMetadata({ 7 | params 8 | }: { 9 | params: Params 10 | }): Promise { 11 | const { locale } = await params; 12 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 13 | const t = translations.common; 14 | 15 | return { 16 | title: t.pages?.privacy?.title || 'Privacy Policy', 17 | description: t.pages?.privacy?.introduction || 'Privacy policy for Tech Notes Hub', 18 | }; 19 | } 20 | 21 | export default async function PrivacyPolicyPage({ 22 | params 23 | }: { 24 | params: Params 25 | }) { 26 | const { locale } = await params; 27 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 28 | const t = translations.common; 29 | 30 | return ( 31 |
32 |

33 | {t.pages?.privacy?.title || 'Privacy Policy'} 34 |

35 | 36 |
37 | {t.pages?.privacy?.lastUpdated || 'Last Updated'}: {new Date('2025-06-06').toLocaleDateString(locale)} 38 |
39 | 40 |
41 |

42 | {t.pages?.privacy?.introduction || 'This Privacy Policy describes how we collect, use, and share your personal information.'} 43 |

44 | 45 |

46 | {t.pages?.privacy?.informationWeCollect || 'Information We Collect'} 47 |

48 |

49 | {t.pages?.privacy?.informationWeCollectText || 'When you visit our website, we may collect certain information about your device.'} 50 |

51 | 52 |

53 | {t.pages?.privacy?.howWeUseInformation || 'How We Use Your Information'} 54 |

55 |

56 | {t.pages?.privacy?.howWeUseInformationText || 'We use the information we collect to improve and optimize our website.'} 57 |

58 | 59 |

60 | {t.pages?.privacy?.sharingYourInformation || 'Sharing Your Information'} 61 |

62 |

63 | {t.pages?.privacy?.sharingYourInformationText || 'We do not share your personal information with third parties.'} 64 |

65 | 66 |

67 | {t.pages?.privacy?.yourRights || 'Your Rights'} 68 |

69 |

70 | {t.pages?.privacy?.yourRightsText || 'You have the right to access personal information we hold about you.'} 71 |

72 |
73 |
74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /website/src/app/[locale]/terms/page.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { Locale, getTranslationsFromNamespaces } from "@/lib/i18n/settings"; 3 | 4 | type Params = Promise<{ locale: Locale }>; 5 | 6 | export async function generateMetadata({ 7 | params 8 | }: { 9 | params: Params 10 | }): Promise { 11 | const { locale } = await params; 12 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 13 | const t = translations.common; 14 | 15 | return { 16 | title: t.pages?.terms?.title || 'Terms of Service', 17 | description: t.pages?.terms?.introduction || 'Terms of service for Tech Notes Hub', 18 | }; 19 | } 20 | 21 | export default async function TermsOfServicePage({ 22 | params 23 | }: { 24 | params: Params 25 | }) { 26 | const { locale } = await params; 27 | const translations = await getTranslationsFromNamespaces(locale, ['common']); 28 | const t = translations.common; 29 | 30 | return ( 31 |
32 |

33 | {t.pages?.terms?.title || 'Terms of Service'} 34 |

35 | 36 |
37 | {t.pages?.terms?.lastUpdated || 'Last Updated'}: {new Date('2025-06-06').toLocaleDateString(locale)} 38 |
39 | 40 |
41 |

42 | {t.pages?.terms?.introduction || 'These Terms of Service govern your access to and use of our website.'} 43 |

44 | 45 |

46 | {t.pages?.terms?.acceptance || 'Acceptance of Terms'} 47 |

48 |

49 | {t.pages?.terms?.acceptanceText || 'By accessing or using our website, you agree to be bound by these Terms.'} 50 |

51 | 52 |

53 | {t.pages?.terms?.intellectualProperty || 'Intellectual Property'} 54 |

55 |

56 | {t.pages?.terms?.intellectualPropertyText || 'The content on our website is owned by us and protected by intellectual property laws.'} 57 |

58 | 59 |

60 | {t.pages?.terms?.userConduct || 'User Conduct'} 61 |

62 |

63 | {t.pages?.terms?.userConductText || 'You agree not to use our website for any illegal or unauthorized purpose.'} 64 |

65 | 66 |

67 | {t.pages?.terms?.disclaimer || 'Disclaimer'} 68 |

69 |

70 | {t.pages?.terms?.disclaimerText || 'Our website is provided "as is" without any warranties.'} 71 |

72 | 73 |

74 | {t.pages?.terms?.limitationOfLiability || 'Limitation of Liability'} 75 |

76 |

77 | {t.pages?.terms?.limitationOfLiabilityText || 'We will not be liable for any damages arising from the use of our website.'} 78 |

79 |
80 |
81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /website/src/app/api/robots.txt/route.ts: -------------------------------------------------------------------------------- 1 | const WEBSITE_URL = 'https://tech-notes-hub.vercel.app'; 2 | 3 | export async function GET() { 4 | const robotsTxt = ` 5 | # Allow all web crawlers 6 | User-agent: * 7 | Allow: / 8 | 9 | # Sitemap location 10 | Sitemap: ${WEBSITE_URL}/api/sitemap.xml 11 | 12 | # Disallow admin paths 13 | User-agent: * 14 | Disallow: /admin/ 15 | Disallow: /api/ 16 | 17 | # Special instructions for specific bots 18 | User-agent: Googlebot 19 | Allow: / 20 | 21 | User-agent: Bingbot 22 | Allow: / 23 | 24 | User-agent: Baiduspider 25 | Disallow: / 26 | 27 | # Crawl-delay suggestion 28 | User-agent: * 29 | Crawl-delay: 10 30 | `.trim(); 31 | 32 | return new Response(robotsTxt, { 33 | headers: { 34 | 'Content-Type': 'text/plain', 35 | }, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /website/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tech-notes-hub/tech-notes/5b24309ca95f8ecd4d0fa8acb9a73e5e1581187b/website/src/app/favicon.ico -------------------------------------------------------------------------------- /website/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import type { Metadata } from "next"; 3 | import { Inter } from "next/font/google"; 4 | import { ThemeProvider } from "@/components/layout/theme-provider"; 5 | import { Toaster } from "sonner"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Tech Notes Hub", 11 | description: "Center for sharing tech knowledge and technical guides", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | 21 | 22 | 28 | {children} 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /website/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect } from 'react'; 4 | import { useRouter } from 'next/navigation'; 5 | import { defaultLocale } from '@/lib/i18n/settings'; 6 | import { setCookie } from '@/lib/cookies'; 7 | 8 | export default function Home() { 9 | const router = useRouter(); 10 | 11 | useEffect(() => { 12 | // Check if the locale is already saved in localStorage 13 | const savedLocale = localStorage.getItem('preferredLocale'); 14 | 15 | if (savedLocale) { 16 | // If there is, use the saved locale 17 | // Also ensure the cookie is set 18 | setCookie('preferredLocale', savedLocale, 365); 19 | router.push(`/${savedLocale}`); 20 | } else { 21 | // If not, try to determine the locale from the browser 22 | const browserLang = navigator.language.split('-')[0]; 23 | const locale = browserLang === 'vi' ? 'vi' : defaultLocale; 24 | 25 | // Save to localStorage for future use 26 | localStorage.setItem('preferredLocale', locale); 27 | 28 | // Also set a cookie for the middleware 29 | setCookie('preferredLocale', locale, 365); 30 | 31 | // Redirect to the page with the determined locale 32 | router.push(`/${locale}`); 33 | } 34 | }, [router]); 35 | 36 | // Show loading while waiting for redirect 37 | return ( 38 |
39 |
40 |
41 |

Loading...

42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /website/src/components/blog/category-filter.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Category } from "@/lib/types"; 4 | import Link from "next/link"; 5 | import { cn } from "@/lib/utils"; 6 | import { Locale } from "@/lib/i18n/settings"; 7 | 8 | interface BlogCategoryFilterProps { 9 | categories: (Category & { count: number })[]; 10 | selectedCategory: string; 11 | locale: Locale; 12 | allCategoriesLabel?: string; 13 | } 14 | 15 | export default function BlogCategoryFilter({ 16 | categories, 17 | selectedCategory, 18 | locale, 19 | allCategoriesLabel = "All Categories" 20 | }: BlogCategoryFilterProps) { 21 | return ( 22 |
23 | 30 | {allCategoriesLabel} 31 | 32 | 33 | {categories.map((category) => ( 34 | 42 | {category.name} 43 | {category.count} 44 | 45 | ))} 46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /website/src/components/blog/code-snippet.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from 'react'; 4 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; 5 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; 6 | import { Copy, Check } from 'lucide-react'; 7 | 8 | interface CodeSnippetProps { 9 | language: string; 10 | content: string; 11 | } 12 | 13 | export default function CodeSnippet({ language, content }: CodeSnippetProps) { 14 | const [copied, setCopied] = useState(false); 15 | 16 | // Map common file extensions to highlight.js language names 17 | const getLanguage = (extension: string): string => { 18 | const langMap: Record = { 19 | 'js': 'javascript', 20 | 'ts': 'typescript', 21 | 'py': 'python', 22 | 'rb': 'ruby', 23 | 'java': 'java', 24 | 'cs': 'csharp', 25 | 'cpp': 'cpp', 26 | 'c': 'c', 27 | 'go': 'go', 28 | 'rs': 'rust', 29 | 'php': 'php', 30 | 'sh': 'bash', 31 | 'yml': 'yaml', 32 | 'yaml': 'yaml', 33 | 'json': 'json', 34 | 'md': 'markdown', 35 | 'html': 'html', 36 | 'css': 'css', 37 | 'scss': 'scss', 38 | 'sql': 'sql', 39 | 'tsx': 'tsx', 40 | 'jsx': 'jsx', 41 | }; 42 | 43 | return langMap[extension.toLowerCase()] || extension; 44 | }; 45 | 46 | const handleCopy = async () => { 47 | try { 48 | await navigator.clipboard.writeText(content); 49 | setCopied(true); 50 | setTimeout(() => setCopied(false), 2000); 51 | } catch (err) { 52 | console.error('Failed to copy text: ', err); 53 | } 54 | }; 55 | 56 | return ( 57 |
58 | 70 | 81 | {content} 82 | 83 |
84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /website/src/components/blog/language-switcher.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Link from 'next/link'; 4 | import { Locale } from '@/lib/i18n/settings'; 5 | import { useEffect } from 'react'; 6 | import { setCookie } from '@/lib/cookies'; 7 | import { Globe } from 'lucide-react'; 8 | 9 | interface LanguageSwitcherProps { 10 | currentLocale: Locale; 11 | translations: any; 12 | availableTranslations: Array<{ 13 | locale: string; 14 | slug: string; 15 | }>; 16 | } 17 | 18 | export default function LanguageSwitcher({ 19 | currentLocale, 20 | translations, 21 | availableTranslations, 22 | }: LanguageSwitcherProps) { 23 | const t = translations.common; 24 | 25 | // Update preferredLocale in localStorage when the component mounts 26 | useEffect(() => { 27 | // Update localStorage and cookie with the current locale 28 | if (typeof window !== 'undefined') { 29 | localStorage.setItem('preferredLocale', currentLocale); 30 | setCookie('preferredLocale', currentLocale, 365); // Valid for 1 year 31 | } 32 | }, [currentLocale]); 33 | 34 | // Handle language change (for analytics or other side effects) 35 | const handleLanguageClick = (newLocale: string) => { 36 | // Update localStorage and cookie 37 | if (typeof window !== 'undefined') { 38 | localStorage.setItem('preferredLocale', newLocale); 39 | setCookie('preferredLocale', newLocale, 365); // Valid for 1 year 40 | } 41 | }; 42 | 43 | if (availableTranslations.length <= 1) { 44 | return null; 45 | } 46 | 47 | return ( 48 |
49 | 50 | {t.blog.translatedContent}: 51 |
52 | {availableTranslations.map(translation => ( 53 | handleLanguageClick(translation.locale)} 57 | className={`text-sm px-2 py-1 rounded cursor-pointer ${ 58 | translation.locale === currentLocale 59 | ? 'bg-primary text-primary-foreground' 60 | : 'bg-secondary text-secondary-foreground hover:bg-secondary/80' 61 | }`} 62 | > 63 | {t.languages[translation.locale as keyof typeof t.languages] || translation.locale.toUpperCase()} 64 | 65 | ))} 66 |
67 |
68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /website/src/components/layout/footer.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ThemeToggle } from "@/components/theme-toggle"; 4 | import { Locale } from "@/lib/i18n/settings"; 5 | import { usePathname } from "next/navigation"; 6 | import Link from "next/link"; 7 | import { Github, Heart } from "lucide-react"; 8 | 9 | interface FooterProps { 10 | translations: any; 11 | locale: Locale; 12 | } 13 | 14 | export function Footer({ translations, locale }: FooterProps) { 15 | const t = translations.common; 16 | const currentYear = new Date().getFullYear(); 17 | const pathname = usePathname(); 18 | 19 | return ( 20 |
21 |
22 |
23 |
24 |

25 | {t.footer?.copyright || `© ${currentYear} Tech Notes Hub. MIT License.`} 26 |

27 |
28 | 29 | 61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /website/src/components/layout/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | 6 | type ThemeProviderProps = { 7 | children: React.ReactNode; 8 | [key: string]: any; 9 | }; 10 | 11 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 12 | return ( 13 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /website/src/components/layout/translation-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { createContext, useContext, ReactNode } from 'react'; 4 | import { Locale } from '@/lib/i18n/settings'; 5 | 6 | type TranslationContextType = { 7 | locale: Locale; 8 | t: (namespace: string, key: string, params?: Record) => string; 9 | translations: Record>; 10 | }; 11 | 12 | const TranslationContext = createContext(undefined); 13 | 14 | export const useTranslation = () => { 15 | const context = useContext(TranslationContext); 16 | if (!context) { 17 | throw new Error('useTranslation must be used within a TranslationProvider'); 18 | } 19 | return context; 20 | }; 21 | 22 | interface TranslationProviderProps { 23 | children: ReactNode; 24 | locale: Locale; 25 | translations: Record>; 26 | } 27 | 28 | export function TranslationProvider({ 29 | children, 30 | locale, 31 | translations 32 | }: TranslationProviderProps) { 33 | const t = (namespace: string, key: string, params?: Record): string => { 34 | try { 35 | const keys = key.split('.'); 36 | let value: any = translations[namespace]; 37 | 38 | for (const k of keys) { 39 | if (!value || !value[k]) return key; 40 | value = value[k]; 41 | } 42 | 43 | if (typeof value === 'string') { 44 | if (params) { 45 | return Object.entries(params).reduce( 46 | (acc: string, [paramKey, paramValue]) => 47 | acc.replace(new RegExp(`{{${paramKey}}}`, 'g'), paramValue), 48 | value 49 | ); 50 | } 51 | return value; 52 | } 53 | 54 | return key; 55 | } catch (error) { 56 | console.error(`Translation error for ${namespace}.${key}`, error); 57 | return key; 58 | } 59 | }; 60 | 61 | return ( 62 | 63 | {children} 64 | 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /website/src/components/search.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState, useEffect, useRef, FormEvent, ChangeEvent } from 'react'; 4 | import { useRouter } from 'next/navigation'; 5 | import { Input } from "@/components/ui/input"; 6 | import { Search as SearchIcon, X } from "lucide-react"; 7 | import { BlogPost } from '@/lib/types'; 8 | import Link from 'next/link'; 9 | import { Locale } from '@/lib/i18n/settings'; 10 | 11 | interface SearchProps { 12 | locale: Locale; 13 | allPosts: BlogPost[]; 14 | placeholder?: string; 15 | } 16 | 17 | export default function Search({ locale, allPosts, placeholder }: SearchProps) { 18 | const [searchTerm, setSearchTerm] = useState(''); 19 | const [results, setResults] = useState([]); 20 | const [isSearchOpen, setIsSearchOpen] = useState(false); 21 | const searchRef = useRef(null); 22 | const router = useRouter(); 23 | 24 | // Close search results when clicking outside 25 | useEffect(() => { 26 | function handleClickOutside(event: MouseEvent) { 27 | if (searchRef.current && !searchRef.current.contains(event.target as Node)) { 28 | setIsSearchOpen(false); 29 | } 30 | } 31 | 32 | document.addEventListener('mousedown', handleClickOutside); 33 | return () => { 34 | document.removeEventListener('mousedown', handleClickOutside); 35 | }; 36 | }, []); 37 | 38 | // Filter posts by title 39 | useEffect(() => { 40 | if (searchTerm.trim() === '') { 41 | setResults([]); 42 | return; 43 | } 44 | 45 | const term = searchTerm.toLowerCase(); 46 | const filtered = allPosts.filter(post => 47 | post.title.toLowerCase().includes(term) 48 | ); 49 | 50 | setResults(filtered); 51 | setIsSearchOpen(true); 52 | }, [searchTerm, allPosts]); 53 | 54 | const handleSearch = (e: FormEvent) => { 55 | e.preventDefault(); 56 | if (results.length > 0) { 57 | router.push(`/${locale}/blog/${results[0].slug}`); 58 | setSearchTerm(''); 59 | setIsSearchOpen(false); 60 | } 61 | }; 62 | 63 | const clearSearch = () => { 64 | setSearchTerm(''); 65 | setResults([]); 66 | }; 67 | 68 | return ( 69 |
70 |
71 | ) => setSearchTerm(e.target.value)} 76 | className="pl-10 pr-10" 77 | onFocus={() => setIsSearchOpen(true)} 78 | /> 79 | 83 | {searchTerm && ( 84 | 91 | )} 92 | 93 | 94 | {isSearchOpen && results.length > 0 && ( 95 |
96 |
97 |

Search Results

98 |
    99 | {results.map(post => ( 100 |
  • 101 | { 105 | setSearchTerm(''); 106 | setIsSearchOpen(false); 107 | }} 108 | > 109 | {post.title} 110 | 111 |
  • 112 | ))} 113 |
114 |
115 |
116 | )} 117 |
118 | ); 119 | } -------------------------------------------------------------------------------- /website/src/components/theme-toggle.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { Moon, Sun } from "lucide-react"; 5 | import { useTheme } from "next-themes"; 6 | 7 | import { Button } from "@/components/ui/button"; 8 | 9 | export function ThemeToggle() { 10 | const { setTheme, theme } = useTheme(); 11 | 12 | return ( 13 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /website/src/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion" 5 | import { ChevronDown } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Accordion = AccordionPrimitive.Root 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )) 21 | AccordionItem.displayName = "AccordionItem" 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 39 | 40 | )) 41 | AccordionTrigger.displayName = "AccordionTrigger" 42 | 43 | const AccordionContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, children, ...props }, ref) => ( 47 | 52 |
{children}
53 |
54 | )) 55 | AccordionContent.displayName = "AccordionContent" 56 | 57 | export { 58 | Accordion, 59 | AccordionItem, 60 | AccordionTrigger, 61 | AccordionContent, 62 | } 63 | -------------------------------------------------------------------------------- /website/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "underline-offset-4 hover:underline text-primary", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /website/src/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | function Card({ className, ...props }: React.ComponentProps<"div">) { 6 | return ( 7 |
15 | ) 16 | } 17 | 18 | function CardHeader({ className, ...props }: React.ComponentProps<"div">) { 19 | return ( 20 |
28 | ) 29 | } 30 | 31 | function CardTitle({ className, ...props }: React.ComponentProps<"div">) { 32 | return ( 33 |
38 | ) 39 | } 40 | 41 | function CardDescription({ className, ...props }: React.ComponentProps<"div">) { 42 | return ( 43 |
48 | ) 49 | } 50 | 51 | function CardAction({ className, ...props }: React.ComponentProps<"div">) { 52 | return ( 53 |
61 | ) 62 | } 63 | 64 | function CardContent({ className, ...props }: React.ComponentProps<"div">) { 65 | return ( 66 |
71 | ) 72 | } 73 | 74 | function CardFooter({ className, ...props }: React.ComponentProps<"div">) { 75 | return ( 76 |
81 | ) 82 | } 83 | 84 | export { 85 | Card, 86 | CardHeader, 87 | CardFooter, 88 | CardTitle, 89 | CardAction, 90 | CardDescription, 91 | CardContent, 92 | } 93 | -------------------------------------------------------------------------------- /website/src/components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DialogPrimitive from "@radix-ui/react-dialog" 5 | import { XIcon } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | function Dialog({ 10 | ...props 11 | }: React.ComponentProps) { 12 | return 13 | } 14 | 15 | function DialogTrigger({ 16 | ...props 17 | }: React.ComponentProps) { 18 | return 19 | } 20 | 21 | function DialogPortal({ 22 | ...props 23 | }: React.ComponentProps) { 24 | return 25 | } 26 | 27 | function DialogClose({ 28 | ...props 29 | }: React.ComponentProps) { 30 | return 31 | } 32 | 33 | function DialogOverlay({ 34 | className, 35 | ...props 36 | }: React.ComponentProps) { 37 | return ( 38 | 46 | ) 47 | } 48 | 49 | function DialogContent({ 50 | className, 51 | children, 52 | showCloseButton = true, 53 | ...props 54 | }: React.ComponentProps & { 55 | showCloseButton?: boolean 56 | }) { 57 | return ( 58 | 59 | 60 | 68 | {children} 69 | {showCloseButton && ( 70 | 74 | 75 | Close 76 | 77 | )} 78 | 79 | 80 | ) 81 | } 82 | 83 | function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { 84 | return ( 85 |
90 | ) 91 | } 92 | 93 | function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { 94 | return ( 95 |
103 | ) 104 | } 105 | 106 | function DialogTitle({ 107 | className, 108 | ...props 109 | }: React.ComponentProps) { 110 | return ( 111 | 116 | ) 117 | } 118 | 119 | function DialogDescription({ 120 | className, 121 | ...props 122 | }: React.ComponentProps) { 123 | return ( 124 | 129 | ) 130 | } 131 | 132 | export { 133 | Dialog, 134 | DialogClose, 135 | DialogContent, 136 | DialogDescription, 137 | DialogFooter, 138 | DialogHeader, 139 | DialogOverlay, 140 | DialogPortal, 141 | DialogTitle, 142 | DialogTrigger, 143 | } 144 | -------------------------------------------------------------------------------- /website/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } -------------------------------------------------------------------------------- /website/src/components/ui/sheet.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SheetPrimitive from "@radix-ui/react-dialog" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | import { X } from "lucide-react" 7 | 8 | import { cn } from "@/lib/utils" 9 | 10 | const Sheet = SheetPrimitive.Root 11 | 12 | const SheetTrigger = SheetPrimitive.Trigger 13 | 14 | const SheetClose = SheetPrimitive.Close 15 | 16 | const SheetPortal = SheetPrimitive.Portal 17 | 18 | const SheetOverlay = React.forwardRef< 19 | React.ElementRef, 20 | React.ComponentPropsWithoutRef 21 | >(({ className, ...props }, ref) => ( 22 | 30 | )) 31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 32 | 33 | const sheetVariants = cva( 34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", 35 | { 36 | variants: { 37 | side: { 38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", 39 | bottom: 40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", 41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", 42 | right: 43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", 44 | }, 45 | }, 46 | defaultVariants: { 47 | side: "right", 48 | }, 49 | } 50 | ) 51 | 52 | interface SheetContentProps 53 | extends React.ComponentPropsWithoutRef, 54 | VariantProps {} 55 | 56 | const SheetContent = React.forwardRef< 57 | React.ElementRef, 58 | SheetContentProps 59 | >(({ side = "right", className, children, ...props }, ref) => ( 60 | 61 | 62 | 67 | {children} 68 | 69 | 70 | Close 71 | 72 | 73 | 74 | )) 75 | SheetContent.displayName = SheetPrimitive.Content.displayName 76 | 77 | const SheetHeader = ({ 78 | className, 79 | ...props 80 | }: React.HTMLAttributes) => ( 81 |
88 | ) 89 | SheetHeader.displayName = "SheetHeader" 90 | 91 | const SheetFooter = ({ 92 | className, 93 | ...props 94 | }: React.HTMLAttributes) => ( 95 |
102 | ) 103 | SheetFooter.displayName = "SheetFooter" 104 | 105 | const SheetTitle = React.forwardRef< 106 | React.ElementRef, 107 | React.ComponentPropsWithoutRef 108 | >(({ className, ...props }, ref) => ( 109 | 114 | )) 115 | SheetTitle.displayName = SheetPrimitive.Title.displayName 116 | 117 | const SheetDescription = React.forwardRef< 118 | React.ElementRef, 119 | React.ComponentPropsWithoutRef 120 | >(({ className, ...props }, ref) => ( 121 | 126 | )) 127 | SheetDescription.displayName = SheetPrimitive.Description.displayName 128 | 129 | export { 130 | Sheet, 131 | SheetPortal, 132 | SheetOverlay, 133 | SheetTrigger, 134 | SheetClose, 135 | SheetContent, 136 | SheetHeader, 137 | SheetFooter, 138 | SheetTitle, 139 | SheetDescription, 140 | } -------------------------------------------------------------------------------- /website/src/config/site.ts: -------------------------------------------------------------------------------- 1 | export type SiteConfig = { 2 | name: string; 3 | description: string; 4 | mainNav: { 5 | title: string; 6 | href: string; 7 | key: string; 8 | }[]; 9 | }; 10 | 11 | export const siteConfig: SiteConfig = { 12 | name: "Tech Notes Hub", 13 | description: "Center for sharing tech knowledge and technical guides", 14 | mainNav: [ 15 | { 16 | title: "Home", 17 | href: "/", 18 | key: "home" 19 | }, 20 | { 21 | title: "Blog", 22 | href: "/blog", 23 | key: "blog" 24 | }, 25 | { 26 | title: "About", 27 | href: "/about", 28 | key: "about" 29 | } 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /website/src/data/i18n/en/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "About Tech Notes Hub", 3 | "intro": "Tech Notes Hub is a comprehensive platform dedicated to sharing technical knowledge and expertise in various technology domains. Our mission is to provide high-quality, detailed technical notes and guides to help developers, engineers, and technology enthusiasts enhance their skills and knowledge.", 4 | "mission": { 5 | "title": "Our Mission", 6 | "content": "We believe in the power of knowledge sharing and collaborative learning. Our goal is to create a hub where technology professionals can find reliable, in-depth technical content that helps them solve problems, learn new skills, and stay updated with the latest developments in the tech industry." 7 | }, 8 | "content": { 9 | "title": "Our Content", 10 | "intro": "Our content covers a wide range of technology topics, including but not limited to:" 11 | }, 12 | "community": { 13 | "title": "Join Our Community", 14 | "content": "We welcome contributions from technology professionals and enthusiasts. If you have expertise in a specific technology area and would like to share your knowledge, please reach out to us." 15 | }, 16 | "contact": { 17 | "title": "Contact Us", 18 | "content": "If you have any questions, suggestions, or would like to contribute to Tech Notes Hub, please don't hesitate to contact us at", 19 | "email": "tanthanhdev@gmail.com" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /website/src/data/i18n/en/home.json: -------------------------------------------------------------------------------- 1 | { 2 | "browseArticles": "Browse Articles", 3 | "aboutHub": "About the Hub", 4 | "featuredTopics": { 5 | "title": "Featured Topics", 6 | "description": "Explore our curated collection of technical topics and in-depth guides", 7 | "viewAllCTA": "View All Articles" 8 | }, 9 | "exploreCollection": "Explore our curated collection of technical topics and in-depth guides", 10 | "hero": { 11 | "title": "Tech Notes Hub", 12 | "description": "Discover technical notes, code snippets, and guides to level up your skills", 13 | "primaryCTA": "Browse Articles", 14 | "secondaryCTA": "About Hub", 15 | "contributeCTA": "Contribute on GitHub" 16 | } 17 | } -------------------------------------------------------------------------------- /website/src/data/i18n/vi/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Giới thiệu về Tech Notes Hub", 3 | "intro": "Tech Notes Hub là một nền tảng toàn diện dành cho việc chia sẻ kiến thức kỹ thuật và chuyên môn trong các lĩnh vực công nghệ khác nhau. Sứ mệnh của chúng tôi là cung cấp các ghi chú kỹ thuật và hướng dẫn chi tiết, chất lượng cao để giúp các nhà phát triển, kỹ sư và những người đam mê công nghệ nâng cao kỹ năng và kiến thức của họ.", 4 | "mission": { 5 | "title": "Sứ mệnh của chúng tôi", 6 | "content": "Chúng tôi tin vào sức mạnh của việc chia sẻ kiến thức và học tập hợp tác. Mục tiêu của chúng tôi là tạo ra một trung tâm nơi các chuyên gia công nghệ có thể tìm thấy nội dung kỹ thuật đáng tin cậy, sâu sắc giúp họ giải quyết vấn đề, học kỹ năng mới và cập nhật các phát triển mới nhất trong ngành công nghiệp công nghệ." 7 | }, 8 | "content": { 9 | "title": "Nội dung của chúng tôi", 10 | "intro": "Nội dung của chúng tôi bao gồm nhiều chủ đề công nghệ, bao gồm nhưng không giới hạn ở:" 11 | }, 12 | "community": { 13 | "title": "Tham gia cộng đồng của chúng tôi", 14 | "content": "Chúng tôi hoan nghênh sự đóng góp từ các chuyên gia và người đam mê công nghệ. Nếu bạn có chuyên môn trong một lĩnh vực công nghệ cụ thể và muốn chia sẻ kiến thức của mình, vui lòng liên hệ với chúng tôi." 15 | }, 16 | "contact": { 17 | "title": "Liên hệ với chúng tôi", 18 | "content": "Nếu bạn có bất kỳ câu hỏi, đề xuất hoặc muốn đóng góp cho Tech Notes Hub, vui lòng liên hệ với chúng tôi tại", 19 | "email": "tanthanhdev@gmail.com" 20 | } 21 | } -------------------------------------------------------------------------------- /website/src/data/i18n/vi/home.json: -------------------------------------------------------------------------------- 1 | { 2 | "browseArticles": "Xem các bài viết", 3 | "aboutHub": "Giới thiệu", 4 | "featuredTopics": { 5 | "title": "Chủ đề nổi bật", 6 | "description": "Khám phá bộ sưu tập các chủ đề kỹ thuật và hướng dẫn chuyên sâu của chúng tôi", 7 | "viewAllCTA": "Xem tất cả bài viết" 8 | }, 9 | "exploreCollection": "Khám phá bộ sưu tập các chủ đề kỹ thuật và hướng dẫn chuyên sâu được tuyển chọn", 10 | "hero": { 11 | "title": "Tech Notes Hub", 12 | "description": "Khám phá các ghi chú kỹ thuật, đoạn mã nguồn và hướng dẫn để nâng cao kỹ năng của bạn", 13 | "primaryCTA": "Xem bài viết", 14 | "secondaryCTA": "Giới thiệu", 15 | "contributeCTA": "Đóng góp trên GitHub" 16 | } 17 | } -------------------------------------------------------------------------------- /website/src/lib/cookies.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Set a cookie with the given name, value, and expiry days 3 | */ 4 | export function setCookie(name: string, value: string, days: number) { 5 | if (typeof document === 'undefined') return; 6 | 7 | let expires = ''; 8 | if (days) { 9 | const date = new Date(); 10 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 11 | expires = `; expires=${date.toUTCString()}`; 12 | } 13 | 14 | document.cookie = `${name}=${value || ''}${expires}; path=/; SameSite=Lax;`; 15 | } 16 | 17 | /** 18 | * Get a cookie value by name 19 | */ 20 | export function getCookie(name: string): string | null { 21 | if (typeof document === 'undefined') return null; 22 | 23 | const nameEQ = `${name}=`; 24 | const ca = document.cookie.split(';'); 25 | 26 | for (let i = 0; i < ca.length; i++) { 27 | let c = ca[i]; 28 | while (c.charAt(0) === ' ') c = c.substring(1, c.length); 29 | if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); 30 | } 31 | 32 | return null; 33 | } 34 | 35 | /** 36 | * Delete a cookie by name 37 | */ 38 | export function deleteCookie(name: string) { 39 | if (typeof document === 'undefined') return; 40 | document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=Lax;`; 41 | } 42 | -------------------------------------------------------------------------------- /website/src/lib/i18n/settings.ts: -------------------------------------------------------------------------------- 1 | export const defaultLocale = 'vi'; 2 | export const locales = ['vi', 'en'] as const; 3 | export type Locale = (typeof locales)[number]; 4 | 5 | export interface CommonTranslations { 6 | site: { 7 | title: string; 8 | description: string; 9 | }; 10 | nav: { 11 | home: string; 12 | blog: string; 13 | about: string; 14 | }; 15 | blog: { 16 | readMore: string; 17 | publishedOn: string; 18 | lastUpdated: string; 19 | by: string; 20 | categories: string; 21 | tags: string; 22 | allCategories: string; 23 | noPostsFound: string; 24 | tryDifferentCategory: string; 25 | source: string; 26 | sourcedFrom: string; 27 | backToBlog: string; 28 | filter: { 29 | category: string; 30 | }; 31 | sourceTypes: { 32 | docs: string; 33 | snippets: string; 34 | i18n: string; 35 | }; 36 | page?: string; 37 | of?: string; 38 | showing?: string; 39 | posts?: string; 40 | translatedContent?: string; 41 | codeSnippets?: string; 42 | postsFound?: string; 43 | searchPlaceholder?: string; 44 | }; 45 | languages: { 46 | english: string; 47 | vietnamese: string; 48 | spanish: string; 49 | }; 50 | common: { 51 | search: string; 52 | loading: string; 53 | notFound: string; 54 | back: string; 55 | }; 56 | footer: { 57 | copyright?: string; 58 | rights?: string; 59 | privacyPolicy?: string; 60 | termsOfService?: string; 61 | links?: { 62 | github: string; 63 | twitter: string; 64 | privacy: string; 65 | terms: string; 66 | }; 67 | madeWith?: string; 68 | }; 69 | categories: { 70 | algorithms: string; 71 | databases: string; 72 | 'design-patterns': string; 73 | devops: string; 74 | linux: string; 75 | 'system-design': string; 76 | testing: string; 77 | }; 78 | pages?: { 79 | privacy?: { 80 | title: string; 81 | lastUpdated: string; 82 | introduction: string; 83 | informationWeCollect: string; 84 | informationWeCollectText: string; 85 | howWeUseInformation: string; 86 | howWeUseInformationText: string; 87 | sharingYourInformation: string; 88 | sharingYourInformationText: string; 89 | yourRights: string; 90 | yourRightsText: string; 91 | }; 92 | terms?: { 93 | title: string; 94 | lastUpdated: string; 95 | introduction: string; 96 | acceptance: string; 97 | acceptanceText: string; 98 | intellectualProperty: string; 99 | intellectualPropertyText: string; 100 | userConduct: string; 101 | userConductText: string; 102 | disclaimer: string; 103 | disclaimerText: string; 104 | limitationOfLiability: string; 105 | limitationOfLiabilityText: string; 106 | }; 107 | }; 108 | } 109 | 110 | export interface Translations { 111 | common: CommonTranslations; 112 | [key: string]: any; 113 | } 114 | 115 | export const getLocalePartsFrom = ({ pathname }: { pathname: string }): { 116 | locale: Locale; 117 | pathnamePart: string; 118 | } => { 119 | const segments = pathname.split('/'); 120 | const localeSegmentIndex = segments.findIndex(segment => 121 | locales.includes(segment as Locale) 122 | ); 123 | 124 | if (localeSegmentIndex === -1) { 125 | return { 126 | locale: defaultLocale, 127 | pathnamePart: pathname, 128 | }; 129 | } 130 | 131 | const locale = segments[localeSegmentIndex] as Locale; 132 | const pathnamePart = `/${segments.slice(localeSegmentIndex + 1).join('/')}`; 133 | 134 | return { 135 | locale, 136 | pathnamePart, 137 | }; 138 | }; 139 | 140 | export const getTranslationsFromNamespaces = async ( 141 | locale: Locale, 142 | namespaces: string[] 143 | ): Promise => { 144 | const translations: Record> = {}; 145 | 146 | for (const namespace of namespaces) { 147 | try { 148 | translations[namespace] = (await import(`../../data/i18n/${locale}/${namespace}.json`)).default; 149 | } catch (error) { 150 | console.error(`Error loading translation for ${locale} and namespace ${namespace}`, error); 151 | translations[namespace] = {}; 152 | } 153 | } 154 | 155 | return translations as Translations; 156 | }; 157 | -------------------------------------------------------------------------------- /website/src/lib/metadata.ts: -------------------------------------------------------------------------------- 1 | import { Metadata } from 'next'; 2 | import { Locale, locales } from './i18n/settings'; 3 | 4 | const WEBSITE_URL = 'https://tech-notes-hub.vercel.app'; 5 | 6 | interface MetadataProps { 7 | title?: string; 8 | description?: string; 9 | locale: Locale; 10 | url?: string; 11 | ogImage?: string; 12 | noIndex?: boolean; 13 | } 14 | 15 | /** 16 | * Generate default metadata for pages with proper SEO settings 17 | */ 18 | export function generateDefaultMetadata({ 19 | title, 20 | description, 21 | locale, 22 | url, 23 | ogImage = '/og-image.jpg', 24 | noIndex = false, 25 | }: MetadataProps): Metadata { 26 | const pageTitle = title ? `${title} - Tech Notes Hub` : 'Tech Notes Hub'; 27 | const pageUrl = url ? `${WEBSITE_URL}${url}` : `${WEBSITE_URL}/${locale}`; 28 | 29 | return { 30 | title: title, 31 | description: description, 32 | robots: { 33 | index: !noIndex, 34 | follow: !noIndex, 35 | googleBot: { 36 | index: !noIndex, 37 | follow: !noIndex, 38 | 'max-video-preview': -1, 39 | 'max-image-preview': 'large', 40 | 'max-snippet': -1, 41 | }, 42 | }, 43 | openGraph: { 44 | title: pageTitle, 45 | description: description, 46 | url: pageUrl, 47 | siteName: 'Tech Notes Hub', 48 | locale: locale, 49 | type: 'website', 50 | images: [ 51 | { 52 | url: `${WEBSITE_URL}${ogImage}`, 53 | width: 1200, 54 | height: 630, 55 | alt: pageTitle, 56 | }, 57 | ], 58 | }, 59 | twitter: { 60 | card: 'summary_large_image', 61 | title: pageTitle, 62 | description: description, 63 | creator: '@technotes', 64 | }, 65 | alternates: { 66 | canonical: pageUrl, 67 | languages: generateLanguageAlternates(url || `/${locale}`), 68 | }, 69 | }; 70 | } 71 | 72 | /** 73 | * Generate language alternates for internationalization 74 | */ 75 | function generateLanguageAlternates(path: string): Record { 76 | const alternates: Record = {}; 77 | const basePath = path.includes('/') ? path.substring(path.indexOf('/')) : ''; 78 | 79 | // If the path already contains a locale, remove it to get the base path 80 | const cleanPath = basePath.match(/^\/(en|vi)\//) 81 | ? basePath.replace(/^\/(en|vi)\//, '/') 82 | : basePath; 83 | 84 | locales.forEach((locale) => { 85 | alternates[locale] = `${WEBSITE_URL}/${locale}${cleanPath}`; 86 | }); 87 | 88 | return alternates; 89 | } 90 | -------------------------------------------------------------------------------- /website/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface BlogPost { 2 | id: string; 3 | title: string; 4 | description: string; 5 | content: string; 6 | slug: string; 7 | date: string; 8 | update?: string; 9 | author: string; 10 | category: string; 11 | tags: string[]; 12 | coverImage?: string; 13 | sourcePath?: string; 14 | sourceType?: 'docs' | 'snippets' | 'i18n'; 15 | language?: string; 16 | relativePath?: string; 17 | availableTranslations?: string[]; 18 | translations?: Record; 19 | relatedSnippets?: Array<{ 20 | path: string; 21 | language: string; 22 | filename: string; 23 | content: string; 24 | }>; 25 | } 26 | 27 | export interface Category { 28 | id: string; 29 | name: string; 30 | description: string; 31 | slug: string; 32 | } 33 | 34 | export interface Tag { 35 | id: string; 36 | name: string; 37 | slug: string; 38 | } 39 | 40 | export interface Author { 41 | id: string; 42 | name: string; 43 | bio: string; 44 | avatar?: string; 45 | } 46 | -------------------------------------------------------------------------------- /website/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /website/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from 'next/server'; 2 | import { defaultLocale, locales } from '@/lib/i18n/settings'; 3 | 4 | // List of countries using Vietnamese as the main language 5 | const VIETNAMESE_COUNTRIES = ['VN', 'VNM']; 6 | 7 | // Cookie name for storing preferred locale 8 | const PREFERRED_LOCALE_COOKIE = 'preferredLocale'; 9 | 10 | // Middleware executed before each request 11 | export function middleware(request: NextRequest) { 12 | // Get the pathname from the URL 13 | const pathname = request.nextUrl.pathname; 14 | 15 | // If pathname starts with /api, /favicon.ico, etc., don't process 16 | if ( 17 | pathname.startsWith('/_next') || 18 | pathname.startsWith('/api') || 19 | pathname.startsWith('/favicon.ico') || 20 | pathname.includes('.') || 21 | pathname.startsWith('/og-image') 22 | ) { 23 | return NextResponse.next(); 24 | } 25 | 26 | // If pathname already has locale, don't process 27 | const pathnameHasLocale = locales.some( 28 | (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}` 29 | ); 30 | 31 | if (pathnameHasLocale) { 32 | return NextResponse.next(); 33 | } 34 | 35 | // Get preferred locale from cookies 36 | const preferredLocale = request.cookies.get(PREFERRED_LOCALE_COOKIE)?.value; 37 | 38 | // If it's the root path, redirect to the appropriate locale 39 | if (pathname === '/') { 40 | // If user has a preferred locale saved, use that 41 | if (preferredLocale && locales.includes(preferredLocale as any)) { 42 | return NextResponse.redirect(new URL(`/${preferredLocale}`, request.url)); 43 | } 44 | 45 | // Otherwise, try to determine locale from country code 46 | const countryCode = 47 | request.headers.get('x-vercel-ip-country') || 48 | request.headers.get('cf-ipcountry') || 49 | request.headers.get('x-country'); 50 | 51 | // If the user is from Vietnam, use Vietnamese 52 | const locale = countryCode && VIETNAMESE_COUNTRIES.includes(countryCode.toUpperCase()) 53 | ? 'vi' 54 | : defaultLocale; 55 | 56 | // Redirect to the appropriate language version 57 | return NextResponse.redirect(new URL(`/${locale}`, request.url)); 58 | } 59 | 60 | // For other paths without locale, check preferred locale first 61 | if (preferredLocale && locales.includes(preferredLocale as any)) { 62 | return NextResponse.redirect(new URL(`/${preferredLocale}${pathname}`, request.url)); 63 | } 64 | 65 | // If no preferred locale, add default locale 66 | const newUrl = new URL(`/${defaultLocale}${pathname}`, request.url); 67 | 68 | // Redirect to URL with default locale 69 | return NextResponse.redirect(newUrl); 70 | } 71 | 72 | // Define which paths will apply middleware 73 | export const config = { 74 | matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], 75 | }; 76 | -------------------------------------------------------------------------------- /website/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface BlogPost { 2 | id: string; 3 | title: string; 4 | description: string; 5 | content: string; 6 | slug: string; 7 | date: string; 8 | author: string; 9 | category: string; 10 | tags: string[]; 11 | coverImage?: string; 12 | sourcePath: string; 13 | sourceType: string; 14 | language: string; 15 | relativePath: string; 16 | relatedSnippets?: Array<{ 17 | path: string; 18 | language: string; 19 | filename: string; 20 | content: string; 21 | }>; 22 | } 23 | -------------------------------------------------------------------------------- /website/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | './pages/**/*.{ts,tsx}', 6 | './components/**/*.{ts,tsx}', 7 | './app/**/*.{ts,tsx}', 8 | './src/**/*.{ts,tsx}', 9 | ], 10 | prefix: "", 11 | theme: { 12 | container: { 13 | center: true, 14 | padding: "2rem", 15 | screens: { 16 | "2xl": "1400px", 17 | }, 18 | }, 19 | extend: { 20 | colors: { 21 | border: "var(--border)", 22 | input: "var(--input)", 23 | ring: "var(--ring)", 24 | background: "var(--background)", 25 | foreground: "var(--foreground)", 26 | primary: { 27 | DEFAULT: "var(--primary)", 28 | foreground: "var(--primary-foreground)", 29 | }, 30 | secondary: { 31 | DEFAULT: "var(--secondary)", 32 | foreground: "var(--secondary-foreground)", 33 | }, 34 | destructive: { 35 | DEFAULT: "var(--destructive)", 36 | foreground: "var(--destructive-foreground)", 37 | }, 38 | muted: { 39 | DEFAULT: "var(--muted)", 40 | foreground: "var(--muted-foreground)", 41 | }, 42 | accent: { 43 | DEFAULT: "var(--accent)", 44 | foreground: "var(--accent-foreground)", 45 | }, 46 | popover: { 47 | DEFAULT: "var(--popover)", 48 | foreground: "var(--popover-foreground)", 49 | }, 50 | card: { 51 | DEFAULT: "var(--card)", 52 | foreground: "var(--card-foreground)", 53 | }, 54 | }, 55 | borderRadius: { 56 | lg: "var(--radius)", 57 | md: "calc(var(--radius) - 2px)", 58 | sm: "calc(var(--radius) - 4px)", 59 | }, 60 | keyframes: { 61 | "accordion-down": { 62 | from: { height: "0" }, 63 | to: { height: "var(--radix-accordion-content-height)" }, 64 | }, 65 | "accordion-up": { 66 | from: { height: "var(--radix-accordion-content-height)" }, 67 | to: { height: "0" }, 68 | }, 69 | }, 70 | animation: { 71 | "accordion-down": "accordion-down 0.2s ease-out", 72 | "accordion-up": "accordion-up 0.2s ease-out", 73 | }, 74 | }, 75 | }, 76 | plugins: [ 77 | require("tailwindcss-animate"), 78 | require("@tailwindcss/typography"), 79 | ], 80 | } 81 | -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | --------------------------------------------------------------------------------