Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05cc3895f1 | ||
|
|
424bfd9090 | ||
|
|
10baf9d489 |
No files matched your search
@@ -4,6 +4,13 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.4.4] - 2026-10-10
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
|
||||||
|
- Cap 登录验证支持新的 hashwx 挑战,旧的 SHA-256 site key 仍可使用
|
||||||
|
- 求解用的 wasm 改由本站提供,不再依赖 jsDelivr
|
||||||
|
|
||||||
## [1.4.3] - 2026-10-09
|
## [1.4.3] - 2026-10-09
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import 'cap-widget'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
apiEndpoint: string
|
apiEndpoint: string
|
||||||
}>()
|
}>()
|
||||||
@@ -13,6 +11,19 @@ const emit = defineEmits<{
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const widgetRef = ref<HTMLElement | null>(null)
|
const widgetRef = ref<HTMLElement | null>(null)
|
||||||
|
const ready = ref(false)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const capWindow = window as Window & {
|
||||||
|
CAP_CUSTOM_WASM_URL?: string
|
||||||
|
CAP_CUSTOM_HASHWX_URL?: string
|
||||||
|
}
|
||||||
|
// SHA-256 与 hashwx 的求解器都放在本站 /cap,避免组件去 jsDelivr 拉 wasm。
|
||||||
|
capWindow.CAP_CUSTOM_WASM_URL = '/cap/cap_wasm_bg.wasm'
|
||||||
|
capWindow.CAP_CUSTOM_HASHWX_URL = '/cap/hashwx.wasm'
|
||||||
|
await import('cap-widget')
|
||||||
|
ready.value = true
|
||||||
|
})
|
||||||
|
|
||||||
function onSolve(event: Event) {
|
function onSolve(event: Event) {
|
||||||
const token = (event as CustomEvent<{ token: string }>).detail?.token
|
const token = (event as CustomEvent<{ token: string }>).detail?.token
|
||||||
@@ -37,6 +48,7 @@ defineExpose({ reset })
|
|||||||
<template>
|
<template>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<cap-widget
|
<cap-widget
|
||||||
|
v-if="ready"
|
||||||
ref="widgetRef"
|
ref="widgetRef"
|
||||||
:data-cap-api-endpoint="props.apiEndpoint"
|
:data-cap-api-endpoint="props.apiEndpoint"
|
||||||
:data-cap-i18n-initial-state="t('auth.capInitialState')"
|
:data-cap-i18n-initial-state="t('auth.capInitialState')"
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ Version history and notable changes. The full log is also in the repo root [CHAN
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.4.4] — 2026-10-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Cap login verification supports hashwx challenges and still accepts older SHA-256 site keys
|
||||||
|
|
||||||
## [1.4.3] — 2026-10-09
|
## [1.4.3] — 2026-10-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ PicHost 版本历史与重要变更说明。完整记录亦见仓库根目录 [C
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.4.4] — 2026-10-10
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
|
||||||
|
- Cap 登录验证支持 hashwx,并继续兼容旧的 SHA-256 site key
|
||||||
|
|
||||||
## [1.4.3] — 2026-10-09
|
## [1.4.3] — 2026-10-09
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
preset: 'node-server',
|
preset: 'node-server',
|
||||||
|
publicAssets: [
|
||||||
|
{
|
||||||
|
baseURL: '/cap',
|
||||||
|
dir: join(rootDir, 'node_modules/@cap.js/wasm/browser'),
|
||||||
|
maxAge: 60 * 60 * 24 * 30
|
||||||
|
}
|
||||||
|
],
|
||||||
scheduledTasks: {
|
scheduledTasks: {
|
||||||
'0 3 * * *': ['auto-delete']
|
'0 3 * * *': ['auto-delete']
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+13
-6
@@ -1,20 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "pichost",
|
"name": "pichost",
|
||||||
"version": "1.4.3",
|
"version": "1.4.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pichost",
|
"name": "pichost",
|
||||||
"version": "1.4.3",
|
"version": "1.4.4",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.876.0",
|
"@aws-sdk/client-s3": "^3.876.0",
|
||||||
|
"@cap.js/wasm": "^0.0.8",
|
||||||
"@iconify-json/lucide": "^1.2.114",
|
"@iconify-json/lucide": "^1.2.114",
|
||||||
"@nuxt/ui": "^4.9.0",
|
"@nuxt/ui": "^4.9.0",
|
||||||
"@nuxtjs/i18n": "^10.6.0",
|
"@nuxtjs/i18n": "^10.6.0",
|
||||||
"cap-widget": "^0.1.57",
|
"cap-widget": "^0.1.58",
|
||||||
"nuxt": "^4.4.8",
|
"nuxt": "^4.4.8",
|
||||||
"sharp": "^0.35.3",
|
"sharp": "^0.35.3",
|
||||||
"tailwindcss": "^4.3.1",
|
"tailwindcss": "^4.3.1",
|
||||||
@@ -1048,6 +1049,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@cap.js/wasm": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@cap.js/wasm/-/wasm-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-rX+4/v2sz4GKLnygXHOS89O7c4GM2qNQcDJP6Y2cyGwoKjg83bsfWHt1TWr9GBzHpFADvju5fvLWYds21nS8sA==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@capsizecss/unpack": {
|
"node_modules/@capsizecss/unpack": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@capsizecss/unpack/-/unpack-4.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@capsizecss/unpack/-/unpack-4.0.1.tgz",
|
||||||
@@ -10730,9 +10737,9 @@
|
|||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
"node_modules/cap-widget": {
|
"node_modules/cap-widget": {
|
||||||
"version": "0.1.57",
|
"version": "0.1.58",
|
||||||
"resolved": "https://registry.npmmirror.com/cap-widget/-/cap-widget-0.1.57.tgz",
|
"resolved": "https://registry.npmmirror.com/cap-widget/-/cap-widget-0.1.58.tgz",
|
||||||
"integrity": "sha512-SyIK5XgYIFSDgH683M9KpfBmDSDB0ZfPZQBxL6MggNrjjnbIQYpbsNpj0S7PC/wXp7oJgAuBD2vtJnN9jJghBg==",
|
"integrity": "sha512-7iS/Q++AaiVvWR8xvMSxRwGWY+fV1ZADJNwPzLk4P1gibCmCbS5G6MGOtoUjsj9OxI9+0nkewW6gRsOGDCOZZA==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/ccount": {
|
"node_modules/ccount": {
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pichost",
|
"name": "pichost",
|
||||||
"version": "1.4.3",
|
"version": "1.4.4",
|
||||||
"description": "Self-hosted image hosting for your NAS / home server — Nuxt admin UI, local disk or S3-compatible storage, server-side WebP compression, referer protection, Twikoo/EasyImage compatible.",
|
"description": "Self-hosted image hosting for your NAS / home server — Nuxt admin UI, local disk or S3-compatible storage, server-side WebP compression, referer protection, Twikoo/EasyImage compatible.",
|
||||||
"homepage": "https://github.com/O96u/PicHost#readme",
|
"homepage": "https://github.com/O96u/PicHost#readme",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -46,10 +46,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.876.0",
|
"@aws-sdk/client-s3": "^3.876.0",
|
||||||
|
"@cap.js/wasm": "^0.0.8",
|
||||||
"@iconify-json/lucide": "^1.2.114",
|
"@iconify-json/lucide": "^1.2.114",
|
||||||
"@nuxt/ui": "^4.9.0",
|
"@nuxt/ui": "^4.9.0",
|
||||||
"@nuxtjs/i18n": "^10.6.0",
|
"@nuxtjs/i18n": "^10.6.0",
|
||||||
"cap-widget": "^0.1.57",
|
"cap-widget": "^0.1.58",
|
||||||
"nuxt": "^4.4.8",
|
"nuxt": "^4.4.8",
|
||||||
"sharp": "^0.35.3",
|
"sharp": "^0.35.3",
|
||||||
"tailwindcss": "^4.3.1",
|
"tailwindcss": "^4.3.1",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { mkdtempSync, rmSync } from 'node:fs'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { afterAll, beforeAll } from 'vitest'
|
||||||
|
import { closeDb } from '../utils/db'
|
||||||
|
|
||||||
|
const workerId = process.env.VITEST_POOL_ID ?? String(process.pid)
|
||||||
|
const dataDir = mkdtempSync(join(tmpdir(), `pichost-test-${workerId}-`))
|
||||||
|
process.env.DATA_DIR = dataDir
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
closeDb()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
closeDb()
|
||||||
|
try {
|
||||||
|
rmSync(dataDir, { recursive: true, force: true })
|
||||||
|
} catch {
|
||||||
|
// Windows may still hold the sqlite file for a moment after close
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -131,7 +131,8 @@ describe('shouldBlockSiteHostImageRequest', () => {
|
|||||||
siteHost,
|
siteHost,
|
||||||
requestHost: siteHost,
|
requestHost: siteHost,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
pathname: '/'
|
pathname: '/',
|
||||||
|
hideFolder: false
|
||||||
})).toBe(false)
|
})).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
import { mkdtempSync, rmSync } from 'node:fs'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { describe, expect, it, vi, beforeAll, beforeEach, afterAll, afterEach } from 'vitest'
|
||||||
|
import { closeDb } from '../db'
|
||||||
import {
|
import {
|
||||||
parseTelegramConfig,
|
parseTelegramConfig,
|
||||||
parseTelegramSecrets,
|
parseTelegramSecrets,
|
||||||
@@ -57,6 +61,23 @@ describe('telegram config', () => {
|
|||||||
describe('TelegramStorageBackend', () => {
|
describe('TelegramStorageBackend', () => {
|
||||||
const backendId = 'tg-test'
|
const backendId = 'tg-test'
|
||||||
const key = 'images/2026/01/test.webp'
|
const key = 'images/2026/01/test.webp'
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), 'pichost-tg-'))
|
||||||
|
const previousDataDir = process.env.DATA_DIR
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
process.env.DATA_DIR = tempDir
|
||||||
|
closeDb()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
closeDb()
|
||||||
|
if (previousDataDir === undefined) {
|
||||||
|
delete process.env.DATA_DIR
|
||||||
|
} else {
|
||||||
|
process.env.DATA_DIR = previousDataDir
|
||||||
|
}
|
||||||
|
rmSync(tempDir, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ensureStorageSchema()
|
ensureStorageSchema()
|
||||||
@@ -75,7 +96,11 @@ describe('TelegramStorageBackend', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
try {
|
||||||
deleteStorageObjectRef(backendId, key)
|
deleteStorageObjectRef(backendId, key)
|
||||||
|
} catch {
|
||||||
|
// schema setup may have failed before the ref table existed
|
||||||
|
}
|
||||||
vi.unstubAllGlobals()
|
vi.unstubAllGlobals()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -3,6 +3,8 @@ import { defineConfig } from 'vitest/config'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
include: ['server/**/*.test.ts']
|
include: ['server/**/*.test.ts'],
|
||||||
|
pool: 'forks',
|
||||||
|
setupFiles: ['./server/test/setup.ts']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Reference in new issue
Block a user