修复显示问题

This commit is contained in:
2026-03-28 18:03:13 +08:00
parent e5611df24e
commit b66ba41431
11 changed files with 139 additions and 27 deletions

View File

@@ -37,6 +37,11 @@ type HTTPHandler struct {
deps Dependencies
}
const (
deviceIDCookieName = "filefast_device_id"
deviceTokenCookieName = "filefast_device_token"
)
func NewHTTPHandler(deps Dependencies) *HTTPHandler {
return &HTTPHandler{deps: deps}
}
@@ -531,8 +536,7 @@ func (h *HTTPHandler) requireAdmin() gin.HandlerFunc {
func (h *HTTPHandler) requireDevice() gin.HandlerFunc {
return func(c *gin.Context) {
deviceID := strings.TrimSpace(c.GetHeader("X-Device-ID"))
token := strings.TrimSpace(c.GetHeader("X-Device-Token"))
deviceID, token := deviceCredentialsFromRequest(c)
if deviceID == "" || token == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing device credentials"})
return
@@ -546,6 +550,27 @@ func (h *HTTPHandler) requireDevice() gin.HandlerFunc {
}
}
func deviceCredentialsFromRequest(c *gin.Context) (string, string) {
deviceID := strings.TrimSpace(c.GetHeader("X-Device-ID"))
token := strings.TrimSpace(c.GetHeader("X-Device-Token"))
if deviceID != "" && token != "" {
return deviceID, token
}
if deviceID == "" {
if value, err := c.Cookie(deviceIDCookieName); err == nil {
deviceID = strings.TrimSpace(value)
}
}
if token == "" {
if value, err := c.Cookie(deviceTokenCookieName); err == nil {
token = strings.TrimSpace(value)
}
}
return deviceID, token
}
func (h *HTTPHandler) authenticatedDeviceID(c *gin.Context) string {
value, ok := c.Get("device_id")
if !ok {

View File

@@ -114,6 +114,26 @@ func TestTransferStatusUpdateRequiresParticipantOwnership(t *testing.T) {
}
}
func TestProtectedRoutesAcceptDeviceCredentialsFromCookies(t *testing.T) {
router, _ := newTestRouter()
device := registerDevice(t, router, map[string]any{
"device_id": "cookie-device",
"name": "Cookie Device",
"type": "desktop",
})
req := httptest.NewRequest(http.MethodGet, "/api/devices/candidates?deviceId="+device.ID, nil)
req.AddCookie(&http.Cookie{Name: deviceIDCookieName, Value: device.ID})
req.AddCookie(&http.Cookie{Name: deviceTokenCookieName, Value: device.AuthToken})
resp := httptest.NewRecorder()
router.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Fatalf("expected cookie-authenticated request to succeed, got %d: %s", resp.Code, resp.Body.String())
}
}
func newTestRouter() (http.Handler, *store.MemoryStore) {
memStore := store.NewMemoryStore(model.RuntimeConfig{})
logger := slog.New(slog.NewTextHandler(io.Discard, nil))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

17
frontend/dist/assets/index-DE3lDjdM.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AirShare Pro</title>
<script type="module" crossorigin src="/assets/index-BhftK8R5.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-jSzxC_eO.css">
<script type="module" crossorigin src="/assets/index-DE3lDjdM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CQ9sinZs.css">
</head>
<body>
<div id="app"></div>

View File

@@ -3,6 +3,9 @@ let deviceSession = {
token: '',
}
const DEVICE_ID_COOKIE = 'filefast_device_id'
const DEVICE_TOKEN_COOKIE = 'filefast_device_token'
function buildDeviceHeaders() {
if (!deviceSession.deviceId || !deviceSession.token) {
return {}
@@ -77,6 +80,8 @@ export function setDeviceSession(deviceId, token) {
deviceId: deviceId || '',
token: token || '',
}
syncDeviceSessionCookies(deviceSession)
}
export function clearDeviceSession() {
@@ -92,3 +97,21 @@ export function withBearerToken(token) {
Authorization: `Bearer ${token}`,
}
}
function syncDeviceSessionCookies(session) {
if (typeof document === 'undefined') {
return
}
writeCookie(DEVICE_ID_COOKIE, session.deviceId)
writeCookie(DEVICE_TOKEN_COOKIE, session.token)
}
function writeCookie(name, value) {
if (!value) {
document.cookie = `${name}=; Path=/; Max-Age=0; SameSite=Lax`
return
}
document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`
}

View File

@@ -72,10 +72,12 @@ function handleEnter() {
rel="noopener noreferrer"
>
<div class="pending-download-copy">
<strong>{{ item.name }}</strong>
<strong :title="item.name">{{ item.name }}</strong>
<p>{{ item.size_label }} · {{ item.created_label }}</p>
</div>
<span class="pending-download-icon" aria-hidden="true">
<LocalIcon name="download" size="18" />
</span>
</a>
</div>
</div>

View File

@@ -515,6 +515,9 @@ a {
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
min-width: 0;
box-sizing: border-box;
padding: 12px 14px;
border-radius: 16px;
background: var(--input-bg);
@@ -534,25 +537,39 @@ a {
}
.pending-download-copy {
flex: 1;
min-width: 0;
overflow: hidden;
}
.pending-download-copy strong {
display: block;
display: -webkit-box;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
white-space: normal;
overflow-wrap: anywhere;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 1.35;
max-height: calc(1.35em * 2);
font-size: 14px;
font-weight: 600;
color: var(--text-main);
}
.pending-download-copy p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: var(--text-secondary);
}
.pending-download-icon {
flex: none;
}
input.room-code,
.text-input-group input,
.text-input-group textarea {
@@ -832,6 +849,9 @@ input.room-code::placeholder {
.batch-item {
margin-bottom: 12px;
width: 100%;
min-width: 0;
box-sizing: border-box;
padding: 16px;
background: var(--item-bg);
border: 1px solid var(--item-border);
@@ -854,11 +874,14 @@ body[data-theme="dark"] .batch-item:hover {
justify-content: space-between;
align-items: center;
gap: 10px;
min-width: 0;
margin-bottom: 12px;
font-size: 14px;
}
.file-info-left {
flex: 1;
min-width: 0;
max-width: 60%;
display: flex;
align-items: center;
@@ -867,6 +890,7 @@ body[data-theme="dark"] .batch-item:hover {
}
.file-icon-wrapper {
flex: none;
width: 32px;
height: 32px;
display: flex;
@@ -878,16 +902,26 @@ body[data-theme="dark"] .batch-item:hover {
}
.file-name {
display: -webkit-box;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
white-space: normal;
overflow-wrap: anywhere;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 1.35;
max-height: calc(1.35em * 2);
font-weight: 500;
color: var(--text-main);
}
.file-info-right {
display: flex;
flex: none;
align-items: center;
min-width: 0;
gap: 8px;
}
@@ -1446,8 +1480,16 @@ body[data-theme="dark"] .batch-item:hover {
}
.file-info-right {
flex-wrap: wrap;
row-gap: 8px;
justify-content: flex-end;
}
.file-status {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
@media (max-width: 600px) {