forked from Mirrors/gomuks
main: add support for logging out
This commit is contained in:
parent
74439be24c
commit
529ffda4ed
12 changed files with 130 additions and 5 deletions
|
@ -148,9 +148,9 @@ func main() {
|
||||||
URL: "/",
|
URL: "/",
|
||||||
})
|
})
|
||||||
|
|
||||||
gmx.Client.EventHandler = hicli.JSONEventHandler(func(command *hicli.JSONCommand) {
|
gmx.SubscribeEvents(nil, func(command *hicli.JSONCommand) {
|
||||||
app.EmitEvent("hicli_event", command)
|
app.EmitEvent("hicli_event", command)
|
||||||
}).HandleEvent
|
})
|
||||||
|
|
||||||
err = app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -178,6 +178,7 @@ func (gmx *Gomuks) StartClient() {
|
||||||
[]byte("meow"),
|
[]byte("meow"),
|
||||||
hicli.JSONEventHandler(gmx.OnEvent).HandleEvent,
|
hicli.JSONEventHandler(gmx.OnEvent).HandleEvent,
|
||||||
)
|
)
|
||||||
|
gmx.Client.LogoutFunc = gmx.Logout
|
||||||
httpClient := gmx.Client.Client.Client
|
httpClient := gmx.Client.Client.Client
|
||||||
httpClient.Transport.(*http.Transport).ForceAttemptHTTP2 = false
|
httpClient.Transport.(*http.Transport).ForceAttemptHTTP2 = false
|
||||||
if !gmx.Config.Matrix.DisableHTTP2 {
|
if !gmx.Config.Matrix.DisableHTTP2 {
|
||||||
|
@ -246,7 +247,9 @@ func (gmx *Gomuks) SubscribeEvents(closeForRestart WebsocketCloseFunc, cb func(c
|
||||||
gmx.nextListenerID++
|
gmx.nextListenerID++
|
||||||
id := gmx.nextListenerID
|
id := gmx.nextListenerID
|
||||||
gmx.eventListeners[id] = cb
|
gmx.eventListeners[id] = cb
|
||||||
gmx.websocketClosers[id] = closeForRestart
|
if closeForRestart != nil {
|
||||||
|
gmx.websocketClosers[id] = closeForRestart
|
||||||
|
}
|
||||||
return func() {
|
return func() {
|
||||||
gmx.eventListenersLock.Lock()
|
gmx.eventListenersLock.Lock()
|
||||||
defer gmx.eventListenersLock.Unlock()
|
defer gmx.eventListenersLock.Unlock()
|
||||||
|
|
64
pkg/gomuks/logout.go
Normal file
64
pkg/gomuks/logout.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// gomuks - A Matrix client written in Go.
|
||||||
|
// Copyright (C) 2024 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package gomuks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (gmx *Gomuks) Logout(ctx context.Context) error {
|
||||||
|
log := zerolog.Ctx(ctx)
|
||||||
|
log.Info().Msg("Stopping client and logging out")
|
||||||
|
gmx.Client.Stop()
|
||||||
|
_, err := gmx.Client.Client.Logout(ctx)
|
||||||
|
if err != nil && !errors.Is(err, mautrix.MUnknownToken) {
|
||||||
|
log.Warn().Err(err).Msg("Failed to log out")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info().Msg("Logout complete, removing data")
|
||||||
|
err = os.RemoveAll(gmx.CacheDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Str("cache_dir", gmx.CacheDir).Msg("Failed to remove cache dir")
|
||||||
|
}
|
||||||
|
if gmx.DataDir == gmx.ConfigDir {
|
||||||
|
err = os.Remove(filepath.Join(gmx.DataDir, "gomuks.db"))
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
log.Err(err).Str("data_dir", gmx.DataDir).Msg("Failed to remove database")
|
||||||
|
}
|
||||||
|
_ = os.Remove(filepath.Join(gmx.DataDir, "gomuks.db-shm"))
|
||||||
|
_ = os.Remove(filepath.Join(gmx.DataDir, "gomuks.db-wal"))
|
||||||
|
} else {
|
||||||
|
err = os.RemoveAll(gmx.DataDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Str("data_dir", gmx.DataDir).Msg("Failed to remove data dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info().Msg("Re-initializing directories")
|
||||||
|
gmx.InitDirectories()
|
||||||
|
log.Info().Msg("Restarting client")
|
||||||
|
gmx.StartClient()
|
||||||
|
gmx.Client.EventHandler(gmx.Client.State())
|
||||||
|
gmx.Client.EventHandler(gmx.Client.SyncStatus.Load())
|
||||||
|
log.Info().Msg("Client restarted")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import (
|
||||||
|
|
||||||
type HiClient struct {
|
type HiClient struct {
|
||||||
DB *database.Database
|
DB *database.Database
|
||||||
|
CryptoDB *dbutil.Database
|
||||||
Account *database.Account
|
Account *database.Account
|
||||||
Client *mautrix.Client
|
Client *mautrix.Client
|
||||||
Crypto *crypto.OlmMachine
|
Crypto *crypto.OlmMachine
|
||||||
|
@ -50,6 +51,7 @@ type HiClient struct {
|
||||||
lastSync time.Time
|
lastSync time.Time
|
||||||
|
|
||||||
EventHandler func(evt any)
|
EventHandler func(evt any)
|
||||||
|
LogoutFunc func(context.Context) error
|
||||||
|
|
||||||
firstSyncReceived bool
|
firstSyncReceived bool
|
||||||
syncingID int
|
syncingID int
|
||||||
|
@ -90,6 +92,9 @@ func New(rawDB, cryptoDB *dbutil.Database, log zerolog.Logger, pickleKey []byte,
|
||||||
|
|
||||||
EventHandler: evtHandler,
|
EventHandler: evtHandler,
|
||||||
}
|
}
|
||||||
|
if cryptoDB != rawDB {
|
||||||
|
c.CryptoDB = cryptoDB
|
||||||
|
}
|
||||||
c.SyncStatus.Store(syncWaiting)
|
c.SyncStatus.Store(syncWaiting)
|
||||||
c.ClientStore = &database.ClientStateStore{Database: db}
|
c.ClientStore = &database.ClientStateStore{Database: db}
|
||||||
c.Client = &mautrix.Client{
|
c.Client = &mautrix.Client{
|
||||||
|
@ -267,4 +272,10 @@ func (h *HiClient) Stop() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.Log.Err(err).Msg("Failed to close database cleanly")
|
h.Log.Err(err).Msg("Failed to close database cleanly")
|
||||||
}
|
}
|
||||||
|
if h.CryptoDB != nil {
|
||||||
|
err = h.CryptoDB.Close()
|
||||||
|
if err != nil {
|
||||||
|
h.Log.Err(err).Msg("Failed to close crypto database cleanly")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,11 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any
|
||||||
return unmarshalAndCall(req.Data, func(params *resolveAliasParams) (*mautrix.RespAliasResolve, error) {
|
return unmarshalAndCall(req.Data, func(params *resolveAliasParams) (*mautrix.RespAliasResolve, error) {
|
||||||
return h.Client.ResolveAlias(ctx, params.Alias)
|
return h.Client.ResolveAlias(ctx, params.Alias)
|
||||||
})
|
})
|
||||||
|
case "logout":
|
||||||
|
if h.LogoutFunc == nil {
|
||||||
|
return nil, errors.New("logout not supported")
|
||||||
|
}
|
||||||
|
return true, h.LogoutFunc(ctx)
|
||||||
case "login":
|
case "login":
|
||||||
return unmarshalAndCall(req.Data, func(params *loginParams) (bool, error) {
|
return unmarshalAndCall(req.Data, func(params *loginParams) (bool, error) {
|
||||||
return true, h.LoginPassword(ctx, params.HomeserverURL, params.Username, params.Password)
|
return true, h.LoginPassword(ctx, params.HomeserverURL, params.Username, params.Password)
|
||||||
|
|
|
@ -293,4 +293,10 @@ export default class Client {
|
||||||
room.paginating = false
|
room.paginating = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
await this.rpc.logout()
|
||||||
|
localStorage.clear()
|
||||||
|
this.store.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,10 @@ export default abstract class RPCClient {
|
||||||
}, this.cancelRequest.bind(this, request_id))
|
}, this.cancelRequest.bind(this, request_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logout(): Promise<boolean> {
|
||||||
|
return this.request("logout", {})
|
||||||
|
}
|
||||||
|
|
||||||
sendMessage(params: SendMessageParams): Promise<RawDBEvent> {
|
sendMessage(params: SendMessageParams): Promise<RawDBEvent> {
|
||||||
return this.request("send_message", params)
|
return this.request("send_message", params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,4 +386,17 @@ export class StateStore {
|
||||||
}
|
}
|
||||||
return { deletedEvents, deletedState } as const
|
return { deletedEvents, deletedState } as const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.rooms.clear()
|
||||||
|
this.roomList.emit([])
|
||||||
|
this.accountData.clear()
|
||||||
|
this.currentRoomListFilter = ""
|
||||||
|
this.#frequentlyUsedEmoji = null
|
||||||
|
this.#emojiPackKeys = null
|
||||||
|
this.#watchedRoomEmojiPacks = null
|
||||||
|
this.#personalEmojiPack = null
|
||||||
|
this.serverPreferenceCache = {}
|
||||||
|
this.activeRoomID = undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ const BeeperLogin = ({ domain, client }: BeeperLoginProps) => {
|
||||||
onChange={onChangeCode}
|
onChange={onChangeCode}
|
||||||
/>}
|
/>}
|
||||||
<button
|
<button
|
||||||
className="beeper-login-button"
|
className="beeper-login-button primary-color-button"
|
||||||
type="submit"
|
type="submit"
|
||||||
>{requestID ? "Submit Code" : "Request Code"}</button>
|
>{requestID ? "Submit Code" : "Request Code"}</button>
|
||||||
{error && <div className="error">
|
{error && <div className="error">
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const VerificationScreen = ({ client, clientState }: LoginScreenProps) =>
|
||||||
value={recoveryKey}
|
value={recoveryKey}
|
||||||
onChange={evt => setRecoveryKey(evt.target.value)}
|
onChange={evt => setRecoveryKey(evt.target.value)}
|
||||||
/>
|
/>
|
||||||
<button className="mx-login-button" type="submit">Verify</button>
|
<button className="mx-login-button primary-color-button" type="submit">Verify</button>
|
||||||
</form>
|
</form>
|
||||||
{error && <div className="error">
|
{error && <div className="error">
|
||||||
{error}
|
{error}
|
||||||
|
|
|
@ -72,4 +72,14 @@ div.settings-view {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.logout {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: var(--error-color);
|
||||||
|
color: var(--inverted-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,6 +259,14 @@ const SettingsView = ({ room }: SettingsViewProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [client, room])
|
}, [client, room])
|
||||||
|
const onClickLogout = useCallback(() => {
|
||||||
|
if (window.confirm("Really log out and delete all local data?")) {
|
||||||
|
client.logout().then(
|
||||||
|
() => console.info("Successfully logged out"),
|
||||||
|
err => window.alert(`Failed to log out: ${err}`),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [client])
|
||||||
usePreferences(client.store, room)
|
usePreferences(client.store, room)
|
||||||
const globalServer = client.store.serverPreferenceCache
|
const globalServer = client.store.serverPreferenceCache
|
||||||
const globalLocal = client.store.localPreferenceCache
|
const globalLocal = client.store.localPreferenceCache
|
||||||
|
@ -293,6 +301,7 @@ const SettingsView = ({ room }: SettingsViewProps) => {
|
||||||
</table>
|
</table>
|
||||||
<CustomCSSInput setPref={setPref} room={room} />
|
<CustomCSSInput setPref={setPref} room={room} />
|
||||||
<AppliedSettingsView room={room} />
|
<AppliedSettingsView room={room} />
|
||||||
|
<button className="logout" onClick={onClickLogout}>Logout</button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue