{"id":31811401,"url":"https://github.com/here-tunan/tunan-blog","last_synced_at":"2025-10-11T06:47:22.595Z","repository":{"id":228482770,"uuid":"774096772","full_name":"here-tunan/tunan-blog","owner":"here-tunan","description":"🚀 A feature-rich, full-stack blog engine built with Go (Fiber) and Next.js (React, Ant Design). Includes a powerful admin panel, Markdown support, and is ready to deploy.","archived":false,"fork":false,"pushed_at":"2025-09-08T02:55:37.000Z","size":177468,"stargazers_count":13,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-08T04:28:54.519Z","etag":null,"topics":["ant-design","blog","dark-mode","fiber","full-stack","go","golang","jwt-auth","markdown","mysql","nextjs","react","rss-feed","self-hosted","sqlite","typescript"],"latest_commit_sha":null,"homepage":"https://www.tunan.fun","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/here-tunan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-18T23:56:25.000Z","updated_at":"2025-09-08T02:55:41.000Z","dependencies_parsed_at":"2024-03-19T02:24:25.214Z","dependency_job_id":"f3d1b46b-d69b-4218-8e5c-4451a514a12b","html_url":"https://github.com/here-tunan/tunan-blog","commit_stats":null,"previous_names":["here-tunan/tunan-blog"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/here-tunan/tunan-blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/here-tunan%2Ftunan-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/here-tunan%2Ftunan-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/here-tunan%2Ftunan-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/here-tunan%2Ftunan-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/here-tunan","download_url":"https://codeload.github.com/here-tunan/tunan-blog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/here-tunan%2Ftunan-blog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006444,"owners_count":26084107,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ant-design","blog","dark-mode","fiber","full-stack","go","golang","jwt-auth","markdown","mysql","nextjs","react","rss-feed","self-hosted","sqlite","typescript"],"created_at":"2025-10-11T06:47:17.613Z","updated_at":"2025-10-11T06:47:22.586Z","avatar_url":"https://github.com/here-tunan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[中文说明](README.zh-CN.md)\n\n# Tunan Blog\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nA full-stack blog application built with Go and Next.js, featuring a modern, feature-rich admin panel and a clean, responsive public-facing interface.\n\n**✨ Perfect for Low-Spec Servers**: This blog system can be fully deployed on minimal server configurations (as low as 1-core, 1GB RAM), making it ideal for developers on a budget or those just starting with self-hosting.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=here-tunan/tunan-blog\u0026type=Date)](https://www.star-history.com/#here-tunan/tunan-blog\u0026Date)\n\n## Live Demo\n\n[https://www.tunan.fun](https://www.tunan.fun)\n\n## Tech Stack\n\n- **Backend**: Go (Golang) with the [Fiber](https://gofiber.io/) web framework.\n- **Frontend (Public)**: [Next.js](https://nextjs.org/) (React), TypeScript, Tailwind CSS.\n- **Frontend (Admin)**: [Next.js](https://nextjs.org/) (React), TypeScript, [Ant Design](https://ant.design/).\n- **Database**: Designed to be compatible with MySQL and SQLite.\n\n### Why Support Both MySQL and SQLite?\n\nMany developers run their projects on servers with limited resources and may not have MySQL database services available. Since blog systems generally have low database requirements, we support SQLite as a lightweight alternative.\n\n## Features\n\n### Public-Facing Site (`/ui`)\n\n- **Blog \u0026 Articles**: Clean and readable interface for blog posts, with full Markdown rendering support.\n- **Command Palette Search**: A fast, keyboard-driven search interface (like VS Code or GitHub) to quickly find articles and navigate the site.\n- **Interactive Comment System**: Integrated with [Remark42](https://remark42.com/) for a privacy-focused, self-hosted commenting experience. For setup instructions, see: [Use remark42 comment system](https://www.tunan.fun/blog/blog-embrace-remark42). We're also considering developing a custom comment system - stay tuned!\n- **View Count Tracking**: Tracks and displays view counts for each article with real client IP detection.\n- **RSS Feed Generation**: Automatically generates RSS feeds with article summaries (optimized for performance).\n- **Social Integration**: Header icons for easy access to GitHub, Discord, Folo, and RSS feeds.\n- **Folo Reader Integration**: Includes specific tracking points and direct links for the [Folo](https://folo.im/) RSS reader.\n- **Book Recommendations**: A dedicated section to display a curated list of books.\n- **Project Showcase**: Display personal or professional projects.\n- **SEO Friendly**: Includes Google Search Console verification and RSS auto-discovery meta tags.\n- **Responsive Design**: Fully responsive layout for an optimal experience on any device.\n- **Dark/Light Theme**: A theme switcher for user preference.\n\n### Admin Panel (`/ui-admin`)\n\n- **Secure Authentication**: JWT-based login system to protect admin routes.\n- **Analytics Dashboard**: View key metrics including page view leaderboard and traffic analytics.\n- **RSS Management**: One-click RSS feed generation and management tools.\n- **Full CRUD for Articles**: Create, Read, Update, and Delete articles with a rich text editor.\n- **Full CRUD for Books**: Easily manage the book recommendation list.\n- **Modern UI**: Built with Ant Design for a professional and intuitive user experience.\n- **Themeable**: Supports both light and dark modes.\n\n## Getting Started\n\nTo get a local copy up and running, follow these simple steps.\n\n### Prerequisites\n\n- Go (version 1.18 or higher)\n- Node.js (version 18 or higher)\n- A running database instance: MySQL (v8.0+) or SQLite3\n- npm or yarn\n\n### Installation \u0026 Setup\n\n1.  **Clone the repository:**\n    ```sh\n    git clone https://github.com/your-username/tunan-blog.git\n    cd tunan-blog\n    ```\n\n2.  **Setup the Database:**\n    - For MySQL, create a new database (e.g., `blog`) and import the table structure:\n      ```sh\n      # Example for MySQL\n      mysql -u your_user -p your_database \u003c schema.sql\n      ```\n    - For SQLite, use the SQLite-specific schema file:\n      ```sh\n      # Example for SQLite\n      sqlite3 your_database.db \u003c schema_sqlite.sql\n      ```\n\n3.  **Configure and Run the Backend:**\n    - Navigate to the project root.\n    - Create and configure your environment file (e.g., `env/dev.yaml`) with your database credentials and other settings.\n    - Run the backend server:\n      ```sh\n      go run cmd/tunan-blog/main.go\n      ```\n    - The backend will be available at `http://localhost:3002`.\n\n4.  **Setup the Public Frontend:**\n    - Navigate to the UI directory:\n      ```sh\n      cd ui\n      ```\n    - Install dependencies:\n      ```sh\n      npm install\n      ```\n    - Run the development server:\n      ```sh\n      npm run dev\n      ```\n    - The public site will be available at `http://localhost:3000`.\n\n5.  **Setup the Admin Panel:**\n    - Navigate to the admin UI directory:\n      ```sh\n      cd ui-admin\n      ```\n    - Install dependencies:\n      ```sh\n      npm install\n      ```\n    - Run the development server:\n      ```sh\n      npm run dev\n      ```\n    - The admin panel will be available at `http://localhost:3001`.\n\n### Performance Notes for Low-Spec Servers\n\nIf you're running on a low-spec server (like 1-core, 1GB RAM), you might encounter issues with `npm install` and `npm run build`. For solutions to common problems like \"npm install ends with 'Killed'\", please see this blog post: [npm install ends with \"Killed\"](https://www.tunan.fun/blog/personal-cloud-server-tools#%E9%85%8D%E7%BD%AE%E5%A4%AA%E4%BD%8E%E5%AF%BC%E8%87%B4%E7%9A%84-npm-install-ends-with-killed-%E7%9A%84%E5%A4%84%E7%90%86).\n\n## Configuration\n\nAll backend configuration is managed via YAML files in the `/env` directory. The application loads `dev.yaml` by default, or `prod.yaml` if the `GO_TUNAN_BLOG_ENV` environment variable is set to `prod`.\n\n### Database Configuration\n\nThe application supports both SQLite and MySQL databases. You can configure which database to use by setting the `database_type` field in your configuration file:\n\n#### Using SQLite (Default)\n```yaml\n# Database type: sqlite3 or mysql  \ndatabase_type: sqlite3  # or leave empty for default\n\nsqlite3:\n  file: /path/to/your/database.db\n```\n\n#### Using MySQL\n```yaml\n# Database type: sqlite3 or mysql\ndatabase_type: mysql\n\nmysql:\n  host: 127.0.0.1\n  port: 3306\n  username: your_username\n  password: your_password\n  database: your_database_name\n```\n\n**Important Notes:**\n- If `database_type` is not specified or empty, SQLite will be used by default\n- Make sure to create your MySQL database before running the application\n- Use the appropriate schema file for your chosen database:\n  - For MySQL: `schema.sql`\n  - For SQLite: `schema_sqlite.sql`\n\n### Admin Authentication\n\nAdmin login is handled by JWT. You need to configure the password and JWT secret in your `.yaml` file.\n\n1.  **Set a temporary password**: In your `dev.yaml` or `prod.yaml`, set a plaintext password for `admin_password` and a random string for `jwt_secret`.\n    ```yaml\n    auth:\n      admin_password: \"your-temporary-strong-password\"\n      jwt_secret: \"a-very-strong-and-secret-string-for-jwt\"\n    ```\n2.  **Generate Password Hash**: Start the backend server (`go run cmd/tunan-blog/main.go`). The application will detect the plaintext password, automatically hash it using bcrypt, and print the hash in the console logs. The log message will look like this:\n    ```\n    IMPORTANT: Please update your dev.yaml with the new hashed password:\n    auth:\n      admin_password: \"$2a$10$....(a long hash string)...\"\n    ```\n3.  **Update Configuration**: Copy the entire hash string (`$2a$...`) from the log and replace your temporary password in the `.yaml` file. This is your permanent, secure password hash.\n4.  **Restart**: Restart the backend server. Now you can log in with the password you set in step 1.\n\n### Google Site Verification\n\nTo verify your ownership of the site with Google Search Console:\n\n1.  **File Location**: The meta tag is located in `ui/app/layout.tsx`.\n2.  **Find the line**:\n    ```html\n    \u003cmeta name=\"google-site-verification\" content=\"xxxx\"/\u003e\n    ```\n3.  **Update the code**: Replace `xxxx` with your own verification code provided by Google.\n\n### Folo Reader Integration\n\nTo configure the Folo RSS reader tracking, update the following fields in your `.yaml` configuration file under the `website` key:\n\n```yaml\nwebsite:\n  # ... other website settings\n  follow_feed_id: \"YOUR_FOLO_FEED_ID\"\n  follow_user_id: \"YOUR_FOLO_USER_ID\"\n```\n\n### RSS Feed Configuration\n\nThe RSS feed is automatically generated with optimized article summaries for better performance:\n\n- **Location**: Generated at `/rss.xml` in the project root\n- **Content**: Article summaries (approximately 200 characters) instead of full content\n- **Auto-Discovery**: Meta tags are automatically included in the HTML head for RSS reader detection\n- **Management**: Use the admin panel dashboard to generate/update the RSS feed with one click\n\n### Social Links Configuration\n\nUpdate the social media links in the navigation component (`ui/app/components/Navigation.tsx`):\n\n- **GitHub**: Update the href in the GitHub icon link\n- **Discord**: Update the Discord server invitation URL\n- **Folo**: Update the Folo user profile URL\n\n### IP Address Tracking\n\nFor accurate visitor IP tracking behind reverse proxies (nginx):\n\n1. **Fiber Configuration**: The app is configured to trust proxy headers\n2. **Nginx Setup**: Ensure your nginx configuration passes the real client IP:\n   ```nginx\n   location /api/ {\n       proxy_pass http://localhost:3002;\n       proxy_set_header X-Real-IP $remote_addr;\n       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n       proxy_set_header X-Forwarded-Proto $scheme;\n       proxy_set_header Host $host;\n   }\n   ```\n\n### Production Deployment with Nginx\n\nIf you want to deploy your blog on your own server, we recommend using Nginx as a reverse proxy. For detailed guidance on Nginx multi-domain configuration and HTTPS certificates, see: [Nginx多域名解析、https证书](https://www.tunan.fun/blog/nginx-proxy-1).\n\n## Project Structure\n\nA brief overview of the key directories in this project:\n\n```\n.\n├── api/          # Go source code for the backend API\n├── cmd/          # Main application entry point for the backend\n├── internal/     # Backend business logic (services, repositories)\n├── ui/           # Next.js source for the public-facing website\n└── ui-admin/     # Next.js source for the admin panel\n```\n\n## Contributing\n\nContributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\n1.  Fork the Project\n2.  Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3.  Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4.  Push to the Branch (`git push origin feature/AmazingFeature`)\n5.  Open a Pull Request\n\n## License\n\nDistributed under the Apache 2.0 License. See `LICENSE` for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhere-tunan%2Ftunan-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhere-tunan%2Ftunan-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhere-tunan%2Ftunan-blog/lists"}