feat: add authentication protected encyclopedia routing, update relevant documents and configurations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -14,6 +14,7 @@ npm run dev
|
||||
```
|
||||
|
||||
默认后端地址为 `http://localhost:8000/api/v1`。
|
||||
开发服务器和本地 Preview 都固定使用 `http://localhost:5173`,与后端默认 CORS 配置保持一致。运行 Preview 前请先停止开发服务器。
|
||||
|
||||
## 检查与构建
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+2
@@ -6,5 +6,7 @@ declare module 'vue-router' {
|
||||
description?: string | ((route: RouteLocationNormalizedLoaded) => string)
|
||||
canonicalPath?: string
|
||||
noindex?: boolean
|
||||
requiresAuth?: boolean
|
||||
requiresAdmin?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user