feat: add authentication protected encyclopedia routing, update relevant documents and configurations

This commit is contained in:
2026-07-18 16:20:51 +08:00
parent 613452b7fe
commit 8da565b36b
7 changed files with 35 additions and 17 deletions
+4 -3
View File
@@ -37,8 +37,8 @@
| `/` | MapView | No |
| `/login`, `/register`, `/forgot-password`, `/auth/confirm`, `/auth/reset-password` | Auth views | No |
| `/upload` | UploadView | Yes |
| `/encyclopedia` | EncyclopediaView | No |
| `/encyclopedia/:id` | CloudTypeView | No |
| `/encyclopedia` | EncyclopediaView | Yes |
| `/encyclopedia/:id` | CloudTypeView | Yes |
| `/gallery` | GalleryView | No |
| `/community` | CommunityView | No |
| `/profile` | ProfileView (own) | Yes |
@@ -109,7 +109,8 @@
## Build & Deploy
- **Vercel** with `vercel.json`: SPA rewrites, security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy). No CSP or HSTS configured.
- **SEO plugin** in `vite.config.ts`: generates `robots.txt` and `sitemap.xml` at build time. Disallows `/admin`, `/auth/`, `/login`, `/register`, `/upload`, `/profile/settings`.
- Vite dev and preview servers both use strict port `5173`, matching the backend's default `CORS_ORIGINS` and `FRONTEND_URL`.
- **SEO plugin** in `vite.config.ts`: generates `robots.txt` and `sitemap.xml` at build time. Auth-only routes, including `/encyclopedia`, are excluded from the sitemap and disallowed in `robots.txt`.
- **`lib/canvas.ts`**: patches `HTMLCanvasElement.getContext('2d')` to always pass `willReadFrequently: true` — needed for the badge card renderer in `lib/cloudBadges.ts`.
## Environment Variables
+1
View File
@@ -14,6 +14,7 @@ npm run dev
```
默认后端地址为 `http://localhost:8000/api/v1`。
开发服务器和本地 Preview 都固定使用 `http://localhost:5173`,与后端默认 CORS 配置保持一致。运行 Preview 前请先停止开发服务器。
## 检查与构建
+4 -2
View File
@@ -193,6 +193,7 @@ async function handleLogout() {
画廊
</RouterLink>
<RouterLink
v-if="authStore.isLoggedIn"
to="/encyclopedia"
class="px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? activeNavClass : inactiveNavClass"
@@ -254,10 +255,10 @@ async function handleLogout() {
</div>
</div>
<div class="mt-3 flex items-center gap-2 text-xs text-slate-500">
<span class="border border-teal-100 bg-teal-50 px-2 py-1 text-teal-700">
<!-- <span class="border border-teal-100 bg-teal-50 px-2 py-1 text-teal-700">
{{ authStore.isAdmin ? '管理员' : '观测者' }}
</span>
<span>OpenCloud 账号</span>
<span>OpenCloud 账号</span> -->
</div>
</div>
@@ -356,6 +357,7 @@ async function handleLogout() {
画廊
</RouterLink>
<RouterLink
v-if="authStore.isLoggedIn"
to="/encyclopedia"
class="shrink-0 px-3 py-2 text-sm font-medium tracking-[0.12em] uppercase transition-colors"
:class="route.name === 'encyclopedia' || route.name === 'cloud-type' ? activeNavClass : inactiveNavClass"
+4
View File
@@ -56,8 +56,10 @@ const router = createRouter({
name: 'encyclopedia',
component: () => import('@/views/encyclopedia/EncyclopediaView.vue'),
meta: {
requiresAuth: true,
title: '云朵图鉴',
description: '浏览 10 种基础云属,了解云型特征,并通过拍摄云图点亮个人图鉴。',
noindex: true,
},
},
{
@@ -65,8 +67,10 @@ const router = createRouter({
name: 'cloud-type',
component: () => import('@/views/encyclopedia/CloudTypeView.vue'),
meta: {
requiresAuth: true,
title: route => getCloudTypeName(route.params.id),
description: route => `查看 ${getCloudTypeName(route.params.id)} 的识别要点、公开云图和社区观测记录。`,
noindex: true,
},
},
{
+2
View File
@@ -6,5 +6,7 @@ declare module 'vue-router' {
description?: string | ((route: RouteLocationNormalizedLoaded) => string)
canonicalPath?: string
noindex?: boolean
requiresAuth?: boolean
requiresAdmin?: boolean
}
}
+11 -11
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { NAlert, NButton, NCard, NDropdown, NEmpty, NIcon, NSkeleton, NTag, useMessage } from 'naive-ui'
import { NAlert, NButton, NCard, NDropdown, NEmpty, NIcon, NSkeleton, useMessage } from 'naive-ui'
import EncyclopediaProgressCard from '@/components/cloud/EncyclopediaProgressCard.vue'
import { Settings } from '@vicons/tabler'
import CloudEditModal, { type CloudEditFormValue } from '@/components/cloud/CloudEditModal.vue'
@@ -70,12 +70,12 @@ const pageTitle = computed(() => {
return isOwnProfile.value ? '我的天空日志' : `${profileData.value?.username || '这位用户'}的天空日志`
})
const profileSubtitle = computed(() => {
if (isOwnProfile.value) {
return '回看你的云图记录、收集进度和拍摄节奏。'
}
return '公开展示的云图记录会按时间顺序陈列在这里。'
})
// const profileSubtitle = computed(() => {
// if (isOwnProfile.value) {
// return '回看你的云图记录、收集进度和拍摄节奏。'
// }
// return '公开展示的云图记录会按时间顺序陈列在这里。'
// })
const totalShots = computed(() => clouds.value.length)
@@ -526,13 +526,13 @@ watch(selectedUploadDate, async newValue => {
</button>
</RouterLink>
</div>
<p class="mt-4 max-w-2xl text-sm leading-7 text-slate-600">
<!-- <p class="mt-4 max-w-2xl text-sm leading-7 text-slate-600">
{{ profileSubtitle }}
</p>
</p> -->
<div class="mt-5 flex flex-wrap items-center gap-3 text-sm text-slate-500">
<span>注册于 {{ formatDate(profileData?.created_at || null) }}</span>
<NTag v-if="isOwnProfile" type="primary" :bordered="false">你的主页</NTag>
<NTag v-else :bordered="false">公开展示</NTag>
<!-- <NTag v-if="isOwnProfile" type="primary" :bordered="false">你的主页</NTag>
<NTag v-else :bordered="false">公开展示</NTag> -->
</div>
</div>
</div>
+9 -1
View File
@@ -6,7 +6,6 @@ import tailwindcss from '@tailwindcss/vite'
const publicRoutes = [
'/',
'/encyclopedia',
'/gallery',
'/community',
]
@@ -32,6 +31,7 @@ function seoFilesPlugin() {
'Disallow: /login',
'Disallow: /register',
'Disallow: /upload',
'Disallow: /encyclopedia',
'Disallow: /profile/settings',
`Sitemap: ${siteUrl}/sitemap.xml`,
'',
@@ -57,6 +57,14 @@ function seoFilesPlugin() {
export default defineConfig({
plugins: [vue(), tailwindcss(), seoFilesPlugin()],
server: {
port: 5173,
strictPort: true,
},
preview: {
port: 5173,
strictPort: true,
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),