Skip to content

Commit 6d6db2e

Browse files
committed
fix: login&logout bug
1 parent 1c52bda commit 6d6db2e

10 files changed

Lines changed: 78 additions & 39 deletions

File tree

QuickDesk/qml/views/MainWindow.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ ApplicationWindow {
434434
text: qsTr("Logout")
435435
iconText: FluentIconGlyph.signOutGlyph
436436
isDestructive: true
437-
onTriggered: root.mainController.authManager.logout()
437+
onTriggered: root.mainController.authManager.logout(root.mainController.deviceId)
438438
}
439439
}
440440
}

QuickDesk/src/controller/MainController.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,10 @@ MainController::MainController(QObject* parent)
164164

165165
// Auth: on login success, auto-bind device + start sync + fetch lists
166166
connect(m_authManager.get(), &AuthManager::loginSuccess, this, [this]() {
167-
// Auto-bind this host device
167+
// Auto-bind this host device (also marks logged_in=true on server)
168168
QString deviceId = m_hostManager->deviceId();
169169
if (!deviceId.isEmpty()) {
170170
m_cloudDeviceManager->autoBindDevice(deviceId);
171-
m_cloudDeviceManager->deviceLogin(deviceId);
172171
}
173172
// Start sync WebSocket
174173
m_cloudDeviceManager->startSync();
@@ -183,19 +182,11 @@ MainController::MainController(QObject* parent)
183182
}
184183
});
185184

186-
// Auth: on logout, notify server device is no longer logged in + stop sync
185+
// Auth: on logout, stop sync (server handles logged_in=false in its logout API)
187186
connect(m_authManager.get(), &AuthManager::loggedOut, this, [this]() {
188187
m_cloudDeviceManager->stopSync();
189188
});
190189

191-
// Notify server before token is cleared (loggedOut fires after token is gone)
192-
connect(m_authManager.get(), &AuthManager::loggingOut, this, [this]() {
193-
QString deviceId = m_hostManager->deviceId();
194-
if (!deviceId.isEmpty()) {
195-
m_cloudDeviceManager->deviceLogout(deviceId);
196-
}
197-
});
198-
199190
// Sync access code changes from cloud devices to recent connections
200191
connect(m_cloudDeviceManager.get(), &CloudDeviceManager::myDevicesChanged, this, [this]() {
201192
for (const auto& v : m_cloudDeviceManager->myDevices()) {
@@ -787,7 +778,6 @@ void MainController::onHostReady(const QString& deviceId, const QString& accessC
787778
// Auto-bind if user is already logged in (loginSuccess may have fired before hostReady)
788779
if (m_authManager->isLoggedIn() && !deviceId.isEmpty()) {
789780
m_cloudDeviceManager->autoBindDevice(deviceId);
790-
m_cloudDeviceManager->deviceLogin(deviceId);
791781
if (!accessCode.isEmpty()) {
792782
m_cloudDeviceManager->syncAccessCode(deviceId, accessCode);
793783
}

QuickDesk/src/manager/AuthManager.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <QJsonDocument>
99
#include <QJsonObject>
1010
#include <QUrl>
11+
#include <QUrlQuery>
1112

1213
namespace quickdesk {
1314

@@ -241,18 +242,23 @@ void AuthManager::loginWithSms(const QString& phone, const QString& smsCode)
241242
});
242243
}
243244

244-
void AuthManager::logout()
245+
void AuthManager::logout(const QString& deviceId)
245246
{
246247
if (m_token.isEmpty()) {
247248
clearSession();
248249
return;
249250
}
250251

251252
QUrl url(httpBaseUrl() + "api/v1/user/logout");
253+
if (!deviceId.isEmpty()) {
254+
QUrlQuery query;
255+
query.addQueryItem("device_id", deviceId);
256+
url.setQuery(query);
257+
}
252258
QList<QPair<QString, QString>> headers;
253259
headers.append(qMakePair(QStringLiteral("Authorization"), QStringLiteral("Bearer ") + m_token));
254260

255-
LOG_INFO("[AuthManager] Logging out user: {}", m_username.toStdString());
261+
LOG_INFO("[AuthManager] Logging out user: {}, device: {}", m_username.toStdString(), deviceId.toStdString());
256262

257263
infra::HttpRequest::instance().sendPostRequest(
258264
url, headers, QString(), kRequestTimeoutMs,
@@ -297,8 +303,6 @@ void AuthManager::fetchUserInfo()
297303

298304
void AuthManager::clearSession()
299305
{
300-
emit loggingOut();
301-
302306
m_isLoggedIn = false;
303307
m_username.clear();
304308
m_userId = 0;

QuickDesk/src/manager/AuthManager.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class AuthManager : public QObject {
3333
Q_INVOKABLE void registerUser(const QString& username, const QString& password,
3434
const QString& phone, const QString& email,
3535
const QString& smsCode = QString());
36-
Q_INVOKABLE void logout();
36+
Q_INVOKABLE void logout(const QString& deviceId = QString());
3737
Q_INVOKABLE void fetchUserInfo();
3838

3939
bool isLoggedIn() const;
@@ -49,7 +49,6 @@ class AuthManager : public QObject {
4949
void loginFailed(const QString& errorCode, const QString& errorMsg);
5050
void registerSuccess();
5151
void registerFailed(const QString& errorCode, const QString& errorMsg);
52-
void loggingOut(); // emitted before token is cleared
5352
void loggedOut();
5453
void smsCodeSent();
5554
void smsCodeFailed(const QString& errorCode, const QString& errorMsg);

SignalingServer/cmd/signaling/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ func main() {
118118
// User authentication (public, no API key required)
119119
userAuth := handler.NewUserAuth(db, redisClient)
120120
userAuth.SetSmsService(smsService)
121+
userAuth.SetLogoutNotifier(func(userID uint, deviceID string) {
122+
wsHandler.NotifyUserSync(userID, map[string]interface{}{
123+
"type": "device_logged_out",
124+
"device_id": deviceID,
125+
})
126+
})
121127
v1.POST("/user/register", userAuth.Register)
122128
v1.POST("/user/login", userAuth.Login)
123129
v1.POST("/user/login-sms", userAuth.LoginWithSms)

SignalingServer/deploy-offline.sh

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,21 @@ fi
9797

9898
echo "Loaded: $LOADED_IMAGE"
9999

100-
# Tag to match what docker-compose.yml expects
101-
EXPECTED_IMAGE=$(grep -E '^\s*image:' docker-compose.yml | head -1 | awk '{print $2}' | tr -d '\r')
102-
if [ -n "$EXPECTED_IMAGE" ] && [ "$LOADED_IMAGE" != "$EXPECTED_IMAGE" ]; then
103-
echo "Tagging $LOADED_IMAGE$EXPECTED_IMAGE"
104-
docker tag "$LOADED_IMAGE" "$EXPECTED_IMAGE"
100+
# Extract tag from loaded image for docker-compose
101+
if echo "$LOADED_IMAGE" | grep -q ':'; then
102+
IMAGE_TAG=$(echo "$LOADED_IMAGE" | grep -oP ':\K.+')
103+
else
104+
# Image has no tag (loaded by ID), tag it as 'latest'
105+
IMAGE_TAG="latest"
106+
EXPECTED="ghcr.io/barry-ran/quickdesk-signaling:latest"
107+
echo "No tag found, tagging as: $EXPECTED"
108+
docker tag "$LOADED_IMAGE" "$EXPECTED"
105109
fi
110+
export IMAGE_TAG
106111

107112
# ---- 2. Start ----
108-
echo "[2/3] Starting services..."
109-
docker compose up -d
113+
echo "[2/3] Starting services (IMAGE_TAG=$IMAGE_TAG)..."
114+
docker compose up -d --force-recreate
110115

111116
# ---- 3. Health check ----
112117
echo "[3/3] Waiting for server to become healthy..."

SignalingServer/deploy-pull.sh

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,14 @@ echo "Port: $PORT"
6666
echo "Domain: ${DOMAIN:-<none>}"
6767
echo ""
6868

69-
# ---- 1. Update image tag in docker-compose.yml ----
70-
if [ "$VERSION" != "latest" ]; then
71-
sed -i "s|image: ${IMAGE_BASE}:.*|image: ${IMAGE_BASE}:${VERSION}|" docker-compose.yml
72-
fi
73-
7469
export SERVER_PORT="$PORT"
70+
export IMAGE_TAG="$VERSION"
7571

76-
# ---- 2. Pull and start ----
77-
echo "[1/3] Pulling image..."
72+
echo "[1/3] Pulling image ${IMAGE_BASE}:${IMAGE_TAG}..."
7873
docker compose pull
7974

8075
echo "[2/3] Starting services..."
81-
docker compose up -d
76+
docker compose up -d --force-recreate
8277

8378
# ---- 3. Health check ----
8479
echo "[3/3] Waiting for server to become healthy..."

SignalingServer/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
signaling-server:
3-
image: ghcr.io/barry-ran/quickdesk-signaling:latest
3+
image: ghcr.io/barry-ran/quickdesk-signaling:${IMAGE_TAG:-latest}
44
container_name: quickdesk-signaling
55
restart: always
66
ports:

SignalingServer/internal/handler/user_device_handler.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ func (h *UserDeviceHandler) AutoBindDevice(c *gin.Context) {
206206
}
207207

208208
// Update device ownership
209-
h.db.Model(&models.Device{}).Where("device_id = ?", req.DeviceID).Update("user_id", authedUserID)
209+
h.db.Model(&models.Device{}).Where("device_id = ?", req.DeviceID).Updates(map[string]interface{}{
210+
"user_id": authedUserID,
211+
"logged_in": true,
212+
})
210213

211214
// Upsert UserDevice: reactivate if exists but inactive, create otherwise
212215
var existing models.UserDevice
@@ -235,6 +238,12 @@ func (h *UserDeviceHandler) AutoBindDevice(c *gin.Context) {
235238

236239
recomputeDeviceCount(h.db, authedUserID)
237240

241+
// Notify other devices this device is now logged in
242+
h.notifySync(authedUserID, gin.H{
243+
"type": "device_logged_in",
244+
"device_id": req.DeviceID,
245+
})
246+
238247
c.JSON(http.StatusOK, gin.H{"message": "设备自动绑定成功", "binding": existing})
239248
}
240249

SignalingServer/internal/handler/user_handler.go

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,16 +263,22 @@ const userTokenTTL = 7 * 24 * time.Hour
263263

264264
// UserAuth manages user session tokens in Redis.
265265
type UserAuth struct {
266-
db *gorm.DB
267-
rdb *redis.Client
268-
sms *service.SmsService
266+
db *gorm.DB
267+
rdb *redis.Client
268+
sms *service.SmsService
269+
logoutNotifier func(userID uint, deviceID string)
269270
}
270271

271272
// NewUserAuth creates a new UserAuth instance.
272273
func NewUserAuth(db *gorm.DB, rdb *redis.Client) *UserAuth {
273274
return &UserAuth{db: db, rdb: rdb}
274275
}
275276

277+
// SetLogoutNotifier sets the callback to notify other devices when a device logs out.
278+
func (a *UserAuth) SetLogoutNotifier(fn func(userID uint, deviceID string)) {
279+
a.logoutNotifier = fn
280+
}
281+
276282
// SetSmsService injects the SMS service (may be nil if SMS is disabled).
277283
func (a *UserAuth) SetSmsService(sms *service.SmsService) {
278284
a.sms = sms
@@ -504,6 +510,31 @@ func (a *UserAuth) Logout(c *gin.Context) {
504510
apiErrorBadRequest(c, CodeInvalidRequest, "token不能为空")
505511
return
506512
}
513+
514+
// Get user ID from token before deleting it
515+
val, err := a.rdb.Get(context.Background(), a.redisKey(token)).Result()
516+
if err == nil {
517+
userID, _ := strconv.ParseUint(val, 10, 64)
518+
if userID > 0 {
519+
// Get device_id from request (optional query param or JSON body)
520+
deviceID := c.Query("device_id")
521+
if deviceID == "" {
522+
var body struct {
523+
DeviceID string `json:"device_id"`
524+
}
525+
c.ShouldBindJSON(&body)
526+
deviceID = body.DeviceID
527+
}
528+
if deviceID != "" {
529+
// Clear logged_in for this specific device
530+
a.db.Model(&models.Device{}).Where("device_id = ? AND user_id = ?", deviceID, userID).Update("logged_in", false)
531+
if a.logoutNotifier != nil {
532+
a.logoutNotifier(uint(userID), deviceID)
533+
}
534+
}
535+
}
536+
}
537+
507538
a.rdb.Del(context.Background(), a.redisKey(token))
508539
c.JSON(http.StatusOK, gin.H{"message": "退出登录成功"})
509540
}

0 commit comments

Comments
 (0)