一个基于 Next.js 的 Mik Casual 服务器官网,支持国际化、主题切换和完整的服务器数据展示。
bun install创建 .env.local 文件:
# Minecraft 服务器 API 地址
MINECRAFT_SERVER_URL=http://your-minecraft-server:8080
# API Key(可选)
MINECRAFT_API_KEY=your-api-key-here
# 建筑服务器 API 地址(可选,不设置则使用 MINECRAFT_SERVER_URL)
BUILDINGS_SERVER_URL=http://your-buildings-server:8080
# 建筑服务器 API Key(可选,不设置则使用 MINECRAFT_API_KEY)
BUILDINGS_API_KEY=your-buildings-api-key-herebun dev访问 http://localhost:3000 查看网站。
bun run build
bun start本项目内置了 4 个 API 路由,用于代理 Minecraft 服务器的数据。所有 API 都支持通过 X-API-Key 请求头传递认证密钥。
端点: GET /api/players
描述: 获取当前在线玩家数量和玩家列表
请求头:
X-API-Key: your-api-key (可选)
响应格式:
{
"count": 3,
"players": [
{
"name": "Steve",
"uuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5"
},
{
"name": "Alex",
"uuid": "8667ba71-b85a-4004-af54-457a9734eed7"
}
]
}字段说明:
count(number): 在线玩家数量players(array): 玩家列表name(string): 玩家名称uuid(string): 玩家 UUID
缓存策略:
- HTTP 缓存:5 秒公共缓存,15 秒 stale-while-revalidate
- 内存缓存:5 秒(在缓存有效期内不会请求 Minecraft 服务器)
- 响应头包含
X-Cache: HIT或X-Cache: MISS标识缓存状态
端点: GET /api/announcements
描述: 获取服务器公告列表
请求头:
X-API-Key: your-api-key (可选)
响应格式:
[
{
"timestamp": 1705305600,
"content": "服务器将于明天进行维护"
},
{
"timestamp": 1705219200,
"content": "欢迎新玩家加入!"
}
]字段说明:
timestamp(number): Unix 时间戳(秒)或 ISO 8601 日期字符串content(string): 公告内容
缓存策略:
- HTTP 缓存:300 秒公共缓存,600 秒 stale-while-revalidate
- 内存缓存:300 秒(在缓存有效期内不会请求 Minecraft 服务器)
- 响应头包含
X-Cache: HIT或X-Cache: MISS标识缓存状态
端点: GET /api/buildings
描述: 获取服务器建筑收录列表
请求头:
X-API-Key: your-api-key (可选)
响应格式:
[
{
"name": {
"zh-CN": "主城大教堂",
"en": "Main Cathedral"
},
"description": {
"zh-CN": "位于主城中心的宏伟建筑",
"en": "A magnificent building in the center"
},
"coordinates": {
"x": 100,
"y": 64,
"z": -200
},
"builders": [
{
"name": "Steve",
"uuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"weight": 100
},
{
"name": "Alex",
"uuid": "8667ba71-b85a-4004-af54-457a9734eed7",
"weight": 50
}
],
"buildType": "original",
"images": [
"/buildings/cathedral-front.png",
"/buildings/cathedral-interior.png",
"/buildings/cathedral-aerial.png"
],
"buildDate": "2024-01-15",
"tags": [
{ "zh-CN": "宗教", "en": "religious" },
{ "zh-CN": "大型", "en": "large" },
{ "zh-CN": "地标", "en": "landmark" }
],
"source": null
},
{
"name": {
"zh-CN": "艾菲尔铁塔复刻",
"en": "Eiffel Tower Replica"
},
"description": {
"zh-CN": "1:1还原的艾菲尔铁塔",
"en": "A 1:1 replica of the Eiffel Tower"
},
"coordinates": {
"x": 800,
"y": 64,
"z": -600
},
"builders": [
{
"name": "Builder123",
"uuid": "f84c6a79-0a4e-45e0-879b-cd49ebd4c4e2",
"weight": 80
},
{
"name": "Helper456",
"uuid": "b0c69a0b-4e9a-4726-a5be-fca90e38aaf5",
"weight": 80
},
{
"name": "Assistant789",
"uuid": "d1e79f4-44e9-4726-a5be-fca90e38aaf5",
"weight": 40
}
],
"buildType": "replica",
"images": [
"/buildings/eiffel-day.png",
"/buildings/eiffel-night.png"
],
"buildDate": "2024-01-28",
"tags": [
{ "zh-CN": "地标", "en": "landmark" },
{ "zh-CN": "大型", "en": "large" },
{ "zh-CN": "历史", "en": "historical" }
],
"source": {
"originalAuthor": "Gustave Eiffel",
"originalLink": "https://www.planetminecraft.com/project/eiffel-tower",
"notes": {
"zh-CN": "基于真实艾菲尔铁塔的1:1复刻",
"en": "Based on the real Eiffel Tower, 1:1 scale"
}
}
}
]字段说明:
name(object): 多语言建筑名称description(object): 多语言建筑描述coordinates(object): 建筑坐标x,y,z(number): 三维坐标
builders(array): 建造者列表(按贡献权重排序)name(string): 建造者名称uuid(string): 建造者 UUIDweight(number): 贡献权重(数值越大表示贡献越大,相同权重视为贡献相等)
buildType(string): 建筑类型original: 原创作品derivative: 二创作品replica: 搬运作品
images(array): 建筑图片数组,支持多张图片- 可以是单张图片的数组(如
["/buildings/image.png"]) - 也可以是多张图片的数组,详情弹窗将显示图片轮播
- 卡片展示使用第一张图片作为封面
- 可以是单张图片的数组(如
buildDate(string): 建造日期(ISO 8601 格式或 Unix 时间戳)tags(array, 可选): 建筑标签数组,支持多语言- 每个标签是一个对象,包含不同语言的翻译
- 格式:
[{ "zh-CN": "中世纪", "en": "medieval" }, { "zh-CN": "大型", "en": "large" }] - 前端会根据当前语言自动显示对应翻译,支持回退到 zh-CN 或第一个可用语言
source(object, 可选): 来源信息(仅非原创作品)originalAuthor(string, 可选): 原作者originalLink(string, 可选): 原作品链接notes(object, 可选): 多语言备注
注意: 建筑数据不包含id字段,前端通过坐标、日期和建造者信息的哈希算法生成唯一标识。
缓存策略:
- HTTP 缓存:300 秒公共缓存,600 秒 stale-while-revalidate
- 内存缓存:300 秒(在缓存有效期内不会请求 Minecraft 服务器)
- 响应头包含
X-Cache: HIT或X-Cache: MISS标识缓存状态
许可证: 所有建筑作品遵循 CC BY-NC-SA 4.0 许可证
端点: GET /api/bans
描述: 获取服务器封禁记录列表
请求头:
X-API-Key: your-api-key (可选)
响应格式:
[
{
"playerName": "Griefer123",
"playerUuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"reason": "恶意破坏他人建筑",
"bannedBy": "Admin",
"bannedAt": "2024-01-15T10:30:00Z",
"expiresAt": null,
"isPermanent": true
},
{
"playerName": "Spammer456",
"playerUuid": "8667ba71-b85a-4004-af54-457a9734eed7",
"reason": "频繁发送垃圾信息",
"bannedBy": "Moderator",
"bannedAt": "2024-02-10T15:45:00Z",
"expiresAt": "2024-03-10T15:45:00Z",
"isPermanent": false
}
]字段说明:
playerName(string): 被封禁玩家名称playerUuid(string): 被封禁玩家 UUID(作为唯一标识)reason(string): 封禁原因bannedBy(string): 执行封禁的管理员bannedAt(string): 封禁时间(ISO 8601 格式)expiresAt(string | null): 解封时间(null 表示永久封禁)isPermanent(boolean): 是否为永久封禁
缓存策略:
- HTTP 缓存:60 秒公共缓存,120 秒 stale-while-revalidate
- 内存缓存:60 秒(在缓存有效期内不会请求 Minecraft 服务器)
- 响应头包含
X-Cache: HIT或X-Cache: MISS标识缓存状态
项目支持中文(zh-CN)和英文(en)两种语言。翻译文件位于 messages/ 目录:
messages/zh-CN.json: 中文翻译messages/en.json: 英文翻译
- 在
messages/目录创建新的语言文件(如ja.json) - 在
i18n/routing.ts中添加新语言到locales数组 - 复制现有翻译文件的结构并翻译内容
项目支持深色和浅色两种主题,使用 CSS 变量实现:
- 深色主题:默认主题,适合夜间浏览
- 浅色主题:使用柔和的阴影替代边框
主题切换按钮位于导航栏右上角。
- 推送代码到 GitHub
- 在 Vercel 导入项目
- 配置环境变量:
MINECRAFT_SERVER_URL: Minecraft 服务器 API 地址MINECRAFT_API_KEY: API 密钥(可选)
- 部署
MikWeb/
├── app/ # Next.js App Router
│ ├── [locale]/ # 国际化路由
│ │ ├── page.tsx # 首页(服务端组件)
│ │ ├── HomeClient.tsx # 首页客户端组件
│ │ ├── layout.tsx # 语言布局
│ │ ├── buildings/ # 建筑收录页
│ │ │ └── page.tsx
│ │ ├── bans/ # 封禁列表页
│ │ │ └── page.tsx
│ │ └── wiki/ # 游戏指南页
│ │ ├── page.tsx
│ │ └── WikiClient.tsx
│ ├── api/ # API 路由(带内存缓存)
│ │ ├── players/ # 玩家 API(5秒缓存)
│ │ │ └── route.ts
│ │ ├── announcements/ # 公告 API(300秒缓存)
│ │ │ └── route.ts
│ │ ├── buildings/ # 建筑 API(300秒缓存)
│ │ │ └── route.ts
│ │ └── bans/ # 封禁 API(60秒缓存)
│ │ └── route.ts
│ ├── layout.tsx # 根布局
│ ├── globals.css # 全局样式
│ ├── manifest.ts # PWA 清单
│ ├── robots.ts # robots.txt
│ └── sitemap.ts # 站点地图
├── components/ # React 组件
│ ├── Navbar.tsx # 导航栏(带玩家列表)
│ ├── Footer.tsx # 页脚
│ ├── Background.tsx # 动态背景
│ ├── MinecraftAvatar.tsx # 玩家头像组件
│ ├── ScrollReveal.tsx # 滚动动画
│ ├── StructuredData.tsx # SEO 结构化数据
│ └── ThemeProvider.tsx # 主题提供者
├── contexts/ # React Context
│ ├── PlayerContext.tsx # 玩家数据全局状态
│ └── BuildingsContext.tsx # 建筑数据全局状态
├── content/ # Wiki 内容(Markdown)
│ ├── zh-CN/ # 中文内容
│ │ ├── getting-started.md
│ │ ├── commands.md
│ │ ├── tips.md
│ │ ├── rules.md
│ │ └── community.md
│ └── en/ # 英文内容
│ ├── getting-started.md
│ ├── commands.md
│ ├── tips.md
│ ├── rules.md
│ └── community.md
├── messages/ # 国际化翻译
│ ├── zh-CN.json # 中文
│ └── en.json # 英文
├── i18n/ # 国际化配置
│ └── routing.ts # 路由配置
├── public/ # 静态资源
│ └── mik-standard-rounded.webp
├── i18n.ts # 国际化主配置
└── next.config.ts # Next.js 配置
- 框架: Next.js 15 (App Router)
- UI 库: React 19
- 语言: TypeScript
- 样式: Tailwind CSS v4
- 图标: Lucide React
- 国际化: next-intl
- 主题: next-themes
- 图片优化: Next.js Image
- 运行时: Bun
编辑 messages/zh-CN.json 和 messages/en.json 修改文本内容。
编辑 components/Navbar.tsx 中的 navItems 数组。
- 在
app/[locale]/目录创建新文件夹 - 添加
page.tsx文件 - 在翻译文件中添加对应文本
- 在 Navbar 中添加导航链接
编辑 app/globals.css 中的 CSS 变量:
:root,
[data-theme="dark"] {
--text-primary: #FFFFFF;
--text-secondary: #FFFFFF;
/* ... 更多变量 */
}
[data-theme="light"] {
--text-primary: #000000;
--text-secondary: #000000;
/* ... 更多变量 */
}- 在
app/api/创建新文件夹 - 添加
route.ts文件 - 实现 GET/POST 等方法
- 配置缓存策略和降级数据
项目使用 MinecraftAvatar 组件自动缓存玩家头像,支持多服务 fallback:
- 主服务: MineSkin
https://mineskin.eu/helm/{uuid}?size={size} - Fallback 1: Minotar
https://minotar.net/avatar/{uuid}/{size} - Fallback 2: MC Heads
https://mc-heads.net/avatar/{uuid}/{size}
当主服务失败时,组件会自动切换到备用服务,确保头像始终可用。