修复显示问题
This commit is contained in:
Binary file not shown.
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
17
frontend/dist/assets/index-BhftK8R5.js
vendored
17
frontend/dist/assets/index-BhftK8R5.js
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-CQ9sinZs.css
vendored
Normal file
1
frontend/dist/assets/index-CQ9sinZs.css
vendored
Normal file
File diff suppressed because one or more lines are too long
17
frontend/dist/assets/index-DE3lDjdM.js
vendored
Normal file
17
frontend/dist/assets/index-DE3lDjdM.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-jSzxC_eO.css
vendored
1
frontend/dist/assets/index-jSzxC_eO.css
vendored
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@@ -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>
|
||||
|
||||
@@ -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`
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user