mirror of
https://github.com/lone-cloud/prism
synced 2026-06-03 08:43:10 -07:00
new eye icon to show/hide proton mail password during initial entry, fix proton re-linking, new release
This commit is contained in:
parent
d5509bb231
commit
4ad7255fc2
9 changed files with 100 additions and 6 deletions
20
Makefile
20
Makefile
|
|
@ -47,7 +47,25 @@ install-tools:
|
|||
echo "signal-cli installed successfully to /usr/local/bin/signal-cli"
|
||||
|
||||
check-updates:
|
||||
@go list -u -m -f '{{if not .Indirect}}{{.Path}} {{.Version}}{{if .Update}} [{{.Update.Version}}]{{end}}{{end}}' all | grep "\[" || echo "All dependencies are up to date"
|
||||
@echo "=== Go module updates ==="
|
||||
@go list -u -m -f '{{if not .Indirect}}{{.Path}} {{.Version}}{{if .Update}} -> {{.Update.Version}}{{end}}{{end}}' all | grep " -> " || echo "All Go dependencies are up to date"
|
||||
@echo ""
|
||||
@echo "=== Dockerfile base image updates ==="
|
||||
@for image in $$(grep -E '^FROM ' Dockerfile | awk '{print $$2}' | grep -v 'AS'); do \
|
||||
echo "Checking $$image..."; \
|
||||
current_digest=$$(docker inspect --format='{{index .RepoDigests 0}}' $$image 2>/dev/null | cut -d@ -f2); \
|
||||
docker pull -q $$image > /dev/null 2>&1; \
|
||||
latest_digest=$$(docker inspect --format='{{index .RepoDigests 0}}' $$image 2>/dev/null | cut -d@ -f2); \
|
||||
if [ -z "$$current_digest" ]; then \
|
||||
echo " $$image: pulled (no prior local image to compare)"; \
|
||||
elif [ "$$current_digest" = "$$latest_digest" ]; then \
|
||||
echo " $$image: up to date"; \
|
||||
else \
|
||||
echo " $$image: UPDATE AVAILABLE"; \
|
||||
echo " local: $$current_digest"; \
|
||||
echo " latest: $$latest_digest"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
release:
|
||||
@if [ ! -f VERSION ]; then \
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.1.0
|
||||
1.1.1
|
||||
|
|
@ -503,6 +503,44 @@ details[open] > .integration-header::before {
|
|||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Password Toggle */
|
||||
.password-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.password-wrapper input {
|
||||
width: 100%;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: var(--text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.password-toggle:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.eye-icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.eye-hide {
|
||||
display: none; /* overridden by inline style after first toggle */
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary,
|
||||
.btn-secondary,
|
||||
|
|
|
|||
|
|
@ -18,9 +18,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
else if (action === 'delete-telegram') deleteTelegram(btn);
|
||||
else if (action === 'delete-proton') deleteProton(btn);
|
||||
else if (action === 'reload') reloadIntegrations();
|
||||
else if (action === 'toggle-password') togglePassword(btn);
|
||||
});
|
||||
});
|
||||
|
||||
function togglePassword(btn) {
|
||||
const input = btn.closest('.password-wrapper').querySelector('input');
|
||||
const isHidden = input.type === 'password';
|
||||
input.type = isHidden ? 'text' : 'password';
|
||||
btn.querySelector('.eye-show').style.display = isHidden ? 'none' : 'block';
|
||||
btn.querySelector('.eye-hide').style.display = isHidden ? 'block' : 'none';
|
||||
btn.setAttribute('aria-label', isHidden ? 'Hide password' : 'Show password');
|
||||
}
|
||||
|
||||
function reloadIntegrations() {
|
||||
const integrations = document.getElementById('integrations');
|
||||
if (integrations) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ func (m *Monitor) authenticateAndSetup(credStore *credentials.Store) error {
|
|||
c := &protonmail.Client{
|
||||
RootURL: protonAPIURL,
|
||||
AppVersion: protonAppVersion,
|
||||
Debug: m.cfg != nil && m.cfg.VerboseLogging,
|
||||
}
|
||||
|
||||
var auth *protonmail.Auth
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func (p *Integration) RegisterRoutes(router *chi.Mux, auth func(http.Handler) ht
|
|||
}
|
||||
}
|
||||
|
||||
RegisterRoutes(router, p.Handlers, auth, p.db, p.apiKey, logger, p)
|
||||
RegisterRoutes(router, p.Handlers, auth, p.db, p.apiKey, logger, p, p.cfg)
|
||||
}
|
||||
|
||||
func (p *Integration) Start(ctx context.Context, logger *slog.Logger) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ type Monitor struct {
|
|||
startTime time.Time
|
||||
consecutiveErrs int
|
||||
lastConnected time.Time
|
||||
cancelPoll context.CancelFunc
|
||||
}
|
||||
|
||||
func NewMonitor(cfg *config.Config, logger *slog.Logger) *Monitor {
|
||||
|
|
@ -40,7 +41,17 @@ func NewMonitor(cfg *config.Config, logger *slog.Logger) *Monitor {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) Stop() {
|
||||
if m.cancelPoll != nil {
|
||||
m.cancelPoll()
|
||||
m.cancelPoll = nil
|
||||
}
|
||||
m.client = nil
|
||||
}
|
||||
|
||||
func (m *Monitor) Start(ctx context.Context, credStore *credentials.Store, publisher *delivery.Publisher) error {
|
||||
m.Stop()
|
||||
|
||||
m.dispatcher = publisher
|
||||
if err := m.authenticateAndSetup(credStore); err != nil {
|
||||
return err
|
||||
|
|
@ -54,7 +65,9 @@ func (m *Monitor) Start(ctx context.Context, credStore *credentials.Store, publi
|
|||
m.lastConnected = time.Now()
|
||||
m.unseenMessageIDs = make(map[string]time.Time)
|
||||
|
||||
go m.pollEvents(ctx)
|
||||
pollCtx, cancel := context.WithCancel(ctx)
|
||||
m.cancelPoll = cancel
|
||||
go m.pollEvents(pollCtx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"prism/service/config"
|
||||
"prism/service/credentials"
|
||||
"prism/service/util"
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ type authHandler struct {
|
|||
apiKey string
|
||||
logger *slog.Logger
|
||||
integration *Integration
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
type protonAuthRequest struct {
|
||||
|
|
@ -46,6 +48,7 @@ func (h *authHandler) handleAuth(w http.ResponseWriter, r *http.Request) {
|
|||
c := &protonmail.Client{
|
||||
RootURL: protonAPIURL,
|
||||
AppVersion: protonAppVersion,
|
||||
Debug: h.cfg != nil && h.cfg.VerboseLogging,
|
||||
}
|
||||
authInfo, err := c.AuthInfo(req.Email)
|
||||
if err != nil {
|
||||
|
|
@ -141,12 +144,16 @@ func (h *authHandler) handleDelete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if h.integration != nil {
|
||||
h.integration.monitor.Stop()
|
||||
}
|
||||
|
||||
util.SetToast(w, "Proton Mail unlinked", "success")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "deleted"})
|
||||
}
|
||||
|
||||
func RegisterRoutes(router *chi.Mux, handlers *Handlers, auth func(http.Handler) http.Handler, db *sql.DB, apiKey string, logger *slog.Logger, integration *Integration) {
|
||||
func RegisterRoutes(router *chi.Mux, handlers *Handlers, auth func(http.Handler) http.Handler, db *sql.DB, apiKey string, logger *slog.Logger, integration *Integration, cfg *config.Config) {
|
||||
if handlers == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -159,6 +166,7 @@ func RegisterRoutes(router *chi.Mux, handlers *Handlers, auth func(http.Handler)
|
|||
apiKey: apiKey,
|
||||
logger: logger,
|
||||
integration: integration,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
router.With(auth).Get("/fragment/proton", handlers.HandleFragment)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proton-password">Password:</label>
|
||||
<div class="password-wrapper">
|
||||
<input type="password" id="proton-password" name="password" placeholder="Your Proton password" required>
|
||||
<button type="button" class="password-toggle" data-action="toggle-password" aria-label="Show password">
|
||||
<svg class="eye-icon eye-show" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
<svg class="eye-icon eye-hide" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-10-7-10-7a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 10 7 10 7a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="proton-totp">2FA Code (optional):</label>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue