Skip to content

Commit 338d5b7

Browse files
committed
实现无密码浏览模式
1 parent 74dcf95 commit 338d5b7

File tree

11 files changed

+222
-96
lines changed

11 files changed

+222
-96
lines changed

README.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,30 @@
66

77
> ✨ 一款极简、自托管的个人导航网站,基于 Go + Vue 开发。
88
9-
**在线体验地址** 👉 [https://nav.hanxi.cc](https://nav.hanxi.cc)
10-
无需账号密码即可访问,请勿修改或删除公共数据 🙏
9+
**在线体验地址** 👉 [https://nav.hanxi.cc](https://nav.hanxi.cc)
10+
11+
- 账号: admin
12+
- 密码: 123456
13+
14+
> [!IMPORTANT]
15+
> 请勿修改或删除数据 🙏
1116
1217
---
1318

19+
## 支持功能
20+
21+
- 拖拽排序
22+
- 夜间模式
23+
- 适配桌面端和移动端
24+
- 拉取网站图标或自定义svg图标
25+
- 无账号密码模式: 不需要账号密码即可编辑
26+
- 无账号密码浏览模式: 不需要账号密码可浏览,需要账号密码才能编辑
27+
1428
## 🐳 使用 Docker 快速部署
1529

1630
### 使用 Docker Compose
1731

18-
#### 国际镜像
32+
#### 国际镜像
1933

2034
```yaml
2135
services:
@@ -32,7 +46,7 @@ services:
3246
- /tiny-nav-data:/app/data
3347
```
3448
35-
#### 国内镜像
49+
#### 国内镜像
3650
3751
```yaml
3852
services:
@@ -57,7 +71,7 @@ docker compose up -d
5771

5872
### 使用 Docker 运行
5973

60-
#### 国际镜像
74+
#### 国际镜像
6175

6276
```bash
6377
docker run -d \
@@ -69,7 +83,7 @@ docker run -d \
6983
hanxi/tiny-nav
7084
```
7185

72-
### 国内镜像
86+
### 国内镜像
7387

7488
```bash
7589
docker run -d \
@@ -87,14 +101,18 @@ docker run -d \
87101

88102
1. 前往 Releases 页面 下载对应平台的可执行文件
89103
2. 无认证启动:
104+
90105
```bash
91106
./tiny-nav --port=58080 --no-auth
92107
```
108+
93109
3. 有账号密码启动:
110+
94111
```bash
95112
./tiny-nav --port=58080 --user=admin --password=123456
96113
````
97-
4. 访问地址:http://localhost:58080
114+
115+
4. 访问地址:<http://localhost:58080>
98116

99117
## 🔧 从源码编译
100118

@@ -108,7 +126,7 @@ sh build.sh
108126
ENABLE_NO_AUTH=true LISTEN_PORT=58080 ./tiny-nav
109127
```
110128

111-
访问:http://localhost:58080
129+
访问:<http://localhost:58080>
112130

113131
## 🧱 技术栈
114132

@@ -117,9 +135,8 @@ ENABLE_NO_AUTH=true LISTEN_PORT=58080 ./tiny-nav
117135

118136
## 📌 开发计划
119137

120-
- [ ] 支持只读模式:查看免登录,编辑需登录
138+
- [x] 支持只读模式:查看免登录,编辑需登录
121139
- [ ] 数据 MD5 对比,避免重复加载
122140
- [ ] 自动深色模式
123141
- [ ] 支持书签导入
124142
- [ ] 支持站内搜索
125-

front/src/api/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useMainStore } from '@/stores'
2-
import type { Link, LoginCredentials, SortIndexUpdate } from './types'
2+
import type { Link, LoginCredentials, SortIndexUpdate, Config } from './types'
33

44
const apiBase = import.meta.env.VITE_API_BASE
55

@@ -108,5 +108,15 @@ export const api = {
108108
method: 'PUT',
109109
body: JSON.stringify({ categories })
110110
})
111-
}
111+
},
112+
113+
async getConfig(): Promise<Config> {
114+
const { data } = await apiFetch<Config>('/config')
115+
return data
116+
},
117+
118+
async validateToken(): Promise<{ status: string }> {
119+
const { data } = await apiFetch<{ status: string }>('/validate')
120+
return data
121+
},
112122
}

front/src/api/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ export interface SortIndexUpdate {
1616
sortIndex: number
1717
category?: string
1818
}
19+
20+
export interface Config {
21+
enableNoAuth: boolean
22+
enableNoAuthView: boolean
23+
}

front/src/components/NavHeader.vue

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818

1919
<!-- PC端按钮组 -->
2020
<div class="hidden md:flex items-center gap-4">
21-
<button @click="toggleEditMode"
21+
<button v-if="showEdit" @click="toggleEditMode"
2222
class="flex items-center gap-2 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700"
2323
:title="editMode ? '浏览模式' : '编辑模式'">
2424
<div
2525
:class="[editMode ? 'i-mdi-eye text-blue-500 dark:text-blue-300' : 'i-mdi-pencil text-gray-400 dark:text-gray-300']">
2626
</div>
2727
</button>
28-
<button @click="$emit('add')"
28+
<button v-if="showEdit" @click="$emit('add')"
2929
class="flex items-center gap-2 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700"
3030
title="添加网站">
3131
<div class="i-mdi-plus-circle text-gray-400 dark:text-gray-300">
@@ -39,26 +39,33 @@
3939
</div>
4040
</button>
4141

42-
<button v-if="!isNoAuthMode" @click="$emit('logout')"
42+
<button v-if="showLogout" @click="handleLogout"
4343
class="flex items-center gap-2 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700"
4444
title="退出登录">
4545
<div class="i-mdi-logout text-gray-400 dark:text-gray-300">
4646
</div>
4747
</button>
48+
49+
<button v-if="showLogin" @click="$emit('login')"
50+
class="flex items-center gap-2 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700"
51+
title="登录">
52+
<div class="i-mdi-login text-gray-400 dark:text-gray-300">
53+
</div>
54+
</button>
4855
</div>
4956
</div>
5057

5158
<!-- 移动端菜单 -->
5259
<div v-if="isMobileMenuOpen" class="md:hidden bg-white dark:bg-gray-800 shadow-lg dark:shadow-gray-900">
5360
<div class="flex flex-col gap-2 p-4">
54-
<button @click="toggleEditMode"
61+
<button v-if="showEdit" @click="toggleEditMode"
5562
class="flex items-center gap-3 px-2 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700">
5663
<div
5764
:class="[editMode ? 'i-mdi-eye text-blue-500 dark:text-blue-300' : 'i-mdi-pencil text-gray-400 dark:text-gray-300']">
5865
</div>
5966
{{ editMode ? '浏览模式' : '编辑模式' }}
6067
</button>
61-
<button @click="$emit('add')"
68+
<button v-if="showEdit" @click="$emit('add')"
6269
class="flex items-center gap-3 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700">
6370
<div class="i-mdi-plus-circle text-gray-400 dark:text-gray-300">
6471
</div>
@@ -71,32 +78,40 @@
7178
</div>
7279
{{ themeStore.isDarkTheme ? '浅色模式' : '深色模式' }}
7380
</button>
74-
<button v-if="!isNoAuthMode" @click="$emit('logout')"
81+
<button v-if="showLogout" @click="$emit('logout')"
82+
class="flex items-center gap-3 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700">
83+
<div class="i-mdi-logout text-gray-400 dark:text-gray-300">
84+
</div>
85+
登出
86+
</button>
87+
<button v-if="showLogin" @click="$emit('login')"
7588
class="flex items-center gap-3 px-3 py-2 rounded-md bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700">
7689
<div class="i-mdi-logout text-gray-400 dark:text-gray-300">
7790
</div>
78-
退出登录
91+
登录
7992
</button>
8093
</div>
8194
</div>
8295
</div>
8396
</template>
8497

8598
<script setup lang="ts">
86-
import { ref } from 'vue'
99+
import { ref, onMounted } from 'vue'
87100
import { useThemeStore } from '@/stores/themeStore'
101+
import { useMainStore } from '@/stores'
88102
89103
const themeStore = useThemeStore()
104+
const store = useMainStore()
90105
91106
const props = defineProps<{
92107
editMode: boolean
93-
isNoAuthMode: boolean
94108
}>()
95109
96110
const emit = defineEmits<{
97111
(e: 'update:editMode', value: boolean): void
98112
(e: 'add'): void
99113
(e: 'logout'): void
114+
(e: 'login'): void
100115
}>()
101116
102117
const toggleEditMode = () => {
@@ -109,4 +124,41 @@ const toggleMobileMenu = () => {
109124
isMobileMenuOpen.value = !isMobileMenuOpen.value
110125
}
111126
127+
const showLogin = ref(false);
128+
const showLogout = ref(false);
129+
const showEdit = ref(false);
130+
131+
async function updateAuthenticationStates() {
132+
// Check if no authentication is needed
133+
if (store.config.enableNoAuth) {
134+
showLogin.value = false;
135+
showLogout.value = false;
136+
showEdit.value = true;
137+
} else {
138+
// Perform async token validation
139+
try {
140+
const isValid = await store.validateToken();
141+
showLogin.value = !isValid;
142+
showLogout.value = isValid;
143+
showEdit.value = isValid;
144+
} catch (error) {
145+
console.error('Error validating token:', error);
146+
showLogin.value = true;
147+
showLogout.value = false;
148+
showEdit.value = false;
149+
}
150+
}
151+
}
152+
153+
// Call the function on component mount
154+
onMounted(() => {
155+
updateAuthenticationStates()
156+
})
157+
158+
const handleLogout = () => {
159+
// Emit logout event
160+
emit('logout')
161+
updateAuthenticationStates()
162+
}
163+
112164
</script>

front/src/main.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import App from './App.vue'
55
import router from './router'
66
import 'virtual:uno.css'
77
import '@unocss/reset/tailwind-compat.css'
8+
import { useMainStore } from '@/stores'
89

910
// 创建应用实例
1011
const app = createApp(App)
@@ -17,4 +18,13 @@ pinia.use(piniaPluginPersistedstate)
1718
app.use(pinia) // Pinia 必须在 router 之前安装
1819
app.use(router)
1920

21+
// 页面加载时调用 fetchConfig
22+
async function initializeApp() {
23+
const store = useMainStore()
24+
const config = await store.fetchConfig()
25+
console.log("Fetched config on load:", config)
26+
}
27+
28+
initializeApp()
29+
2030
app.mount('#app')

front/src/router/index.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ const router = createRouter({
55
history: createWebHashHistory(import.meta.env.BASE_URL),
66
routes: [
77
{
8-
path: '/',
8+
path: '/login',
99
name: 'login',
1010
component: () => import('@/views/Login.vue')
1111
},
1212
{
13-
path: '/nav',
13+
path: '/',
1414
name: 'nav',
1515
component: () => import('@/views/Nav.vue'),
1616
meta: { requiresAuth: true }
@@ -19,25 +19,26 @@ const router = createRouter({
1919
})
2020

2121
router.beforeEach(async (to, _) => {
22-
const store = useMainStore()
23-
2422
// 如果路由需要认证
2523
if (to.meta.requiresAuth) {
26-
// 验证 token
27-
const isValid = await store.validateToken()
28-
29-
if (!isValid) {
30-
// token 无效,重定向到登录页
31-
return { name: 'login' }
24+
const store = useMainStore()
25+
let needAuth = true
26+
if (to.name === 'nav') {
27+
const config = store.config
28+
if (config.enableNoAuth || config.enableNoAuthView) {
29+
needAuth = false
30+
}
3231
}
33-
} else if (to.name === 'login') {
34-
// 如果要去登录页,先检查是否有有效token
35-
const isValid = await store.validateToken()
32+
if (needAuth) {
33+
// 验证 token
34+
const isValid = await store.validateToken()
3635

37-
if (isValid) {
38-
// 如果 token 有效,直接跳转到导航页
39-
return { name: 'nav' }
36+
if (!isValid) {
37+
// token 无效,重定向到登录页
38+
return { name: 'login' }
39+
}
4040
}
41+
console.log('需要认证的路由:', to.name)
4142
}
4243
})
4344

0 commit comments

Comments
 (0)