Remove awkward subscription id
This commit is contained in:
parent
9131d3d521
commit
cc309e87e9
12 changed files with 67 additions and 104 deletions
|
@ -76,6 +76,7 @@ var (
|
||||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
||||||
errHTTPConflictUserExists = &errHTTP{40901, http.StatusConflict, "conflict: user already exists", ""}
|
errHTTPConflictUserExists = &errHTTP{40901, http.StatusConflict, "conflict: user already exists", ""}
|
||||||
errHTTPConflictTopicReserved = &errHTTP{40902, http.StatusConflict, "conflict: access control entry for topic or topic pattern already exists", ""}
|
errHTTPConflictTopicReserved = &errHTTP{40902, http.StatusConflict, "conflict: access control entry for topic or topic pattern already exists", ""}
|
||||||
|
errHTTPConflictSubscriptionExists = &errHTTP{40903, http.StatusConflict, "conflict: topic subscription already exists", ""}
|
||||||
errHTTPEntityTooLargeAttachment = &errHTTP{41301, http.StatusRequestEntityTooLarge, "attachment too large, or bandwidth limit reached", "https://ntfy.sh/docs/publish/#limitations"}
|
errHTTPEntityTooLargeAttachment = &errHTTP{41301, http.StatusRequestEntityTooLarge, "attachment too large, or bandwidth limit reached", "https://ntfy.sh/docs/publish/#limitations"}
|
||||||
errHTTPEntityTooLargeMatrixRequest = &errHTTP{41302, http.StatusRequestEntityTooLarge, "Matrix request is larger than the max allowed length", ""}
|
errHTTPEntityTooLargeMatrixRequest = &errHTTP{41302, http.StatusRequestEntityTooLarge, "Matrix request is larger than the max allowed length", ""}
|
||||||
errHTTPEntityTooLargeJSONBody = &errHTTP{41303, http.StatusRequestEntityTooLarge, "JSON body too large", ""}
|
errHTTPEntityTooLargeJSONBody = &errHTTP{41303, http.StatusRequestEntityTooLarge, "JSON body too large", ""}
|
||||||
|
|
|
@ -39,8 +39,6 @@ import (
|
||||||
- tiers
|
- tiers
|
||||||
- api
|
- api
|
||||||
- tokens
|
- tokens
|
||||||
- LOW: UI: Flickering upgrade banner when logging in
|
|
||||||
- LOW: get rid of reservation id, replace with DELETE X-Topic: ...
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -98,7 +96,6 @@ var (
|
||||||
apiAccountBillingSubscriptionCheckoutSuccessTemplate = "/v1/account/billing/subscription/success/{CHECKOUT_SESSION_ID}"
|
apiAccountBillingSubscriptionCheckoutSuccessTemplate = "/v1/account/billing/subscription/success/{CHECKOUT_SESSION_ID}"
|
||||||
apiAccountBillingSubscriptionCheckoutSuccessRegex = regexp.MustCompile(`/v1/account/billing/subscription/success/(.+)$`)
|
apiAccountBillingSubscriptionCheckoutSuccessRegex = regexp.MustCompile(`/v1/account/billing/subscription/success/(.+)$`)
|
||||||
apiAccountReservationSingleRegex = regexp.MustCompile(`/v1/account/reservation/([-_A-Za-z0-9]{1,64})$`)
|
apiAccountReservationSingleRegex = regexp.MustCompile(`/v1/account/reservation/([-_A-Za-z0-9]{1,64})$`)
|
||||||
apiAccountSubscriptionSingleRegex = regexp.MustCompile(`^/v1/account/subscription/([-_A-Za-z0-9]{16})$`)
|
|
||||||
staticRegex = regexp.MustCompile(`^/static/.+`)
|
staticRegex = regexp.MustCompile(`^/static/.+`)
|
||||||
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
||||||
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
||||||
|
@ -404,9 +401,9 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
||||||
return s.ensureUser(s.withAccountSync(s.handleAccountSettingsChange))(w, r, v)
|
return s.ensureUser(s.withAccountSync(s.handleAccountSettingsChange))(w, r, v)
|
||||||
} else if r.Method == http.MethodPost && r.URL.Path == apiAccountSubscriptionPath {
|
} else if r.Method == http.MethodPost && r.URL.Path == apiAccountSubscriptionPath {
|
||||||
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionAdd))(w, r, v)
|
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionAdd))(w, r, v)
|
||||||
} else if r.Method == http.MethodPatch && apiAccountSubscriptionSingleRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodPatch && r.URL.Path == apiAccountSubscriptionPath {
|
||||||
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionChange))(w, r, v)
|
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionChange))(w, r, v)
|
||||||
} else if r.Method == http.MethodDelete && apiAccountSubscriptionSingleRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodDelete && r.URL.Path == apiAccountSubscriptionPath {
|
||||||
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionDelete))(w, r, v)
|
return s.ensureUser(s.withAccountSync(s.handleAccountSubscriptionDelete))(w, r, v)
|
||||||
} else if r.Method == http.MethodPost && r.URL.Path == apiAccountReservationPath {
|
} else if r.Method == http.MethodPost && r.URL.Path == apiAccountReservationPath {
|
||||||
return s.ensureUser(s.withAccountSync(s.handleAccountReservationAdd))(w, r, v)
|
return s.ensureUser(s.withAccountSync(s.handleAccountReservationAdd))(w, r, v)
|
||||||
|
|
|
@ -12,8 +12,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
subscriptionIDLength = 16
|
|
||||||
subscriptionIDPrefix = "su_"
|
|
||||||
syncTopicAccountSyncEvent = "sync"
|
syncTopicAccountSyncEvent = "sync"
|
||||||
tokenExpiryDuration = 72 * time.Hour // Extend tokens by this much
|
tokenExpiryDuration = 72 * time.Hour // Extend tokens by this much
|
||||||
)
|
)
|
||||||
|
@ -323,52 +321,36 @@ func (s *Server) handleAccountSubscriptionAdd(w http.ResponseWriter, r *http.Req
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u := v.User()
|
u := v.User()
|
||||||
if u.Prefs == nil {
|
prefs := u.Prefs
|
||||||
u.Prefs = &user.Prefs{}
|
if prefs == nil {
|
||||||
|
prefs = &user.Prefs{}
|
||||||
}
|
}
|
||||||
newSubscription.ID = "" // Client cannot set ID
|
for _, subscription := range prefs.Subscriptions {
|
||||||
for _, subscription := range u.Prefs.Subscriptions {
|
|
||||||
if newSubscription.BaseURL == subscription.BaseURL && newSubscription.Topic == subscription.Topic {
|
if newSubscription.BaseURL == subscription.BaseURL && newSubscription.Topic == subscription.Topic {
|
||||||
newSubscription = subscription
|
return errHTTPConflictSubscriptionExists
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newSubscription.ID == "" {
|
prefs.Subscriptions = append(prefs.Subscriptions, newSubscription)
|
||||||
newSubscription.ID = util.RandomStringPrefix(subscriptionIDPrefix, subscriptionIDLength)
|
logvr(v, r).Tag(tagAccount).With(newSubscription).Debug("Adding subscription for user %s", u.Name)
|
||||||
prefs := u.Prefs
|
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
||||||
prefs.Subscriptions = append(prefs.Subscriptions, newSubscription)
|
return err
|
||||||
logvr(v, r).
|
|
||||||
Tag(tagAccount).
|
|
||||||
Fields(log.Context{
|
|
||||||
"base_url": newSubscription.BaseURL,
|
|
||||||
"topic": newSubscription.Topic,
|
|
||||||
}).
|
|
||||||
Debug("Adding subscription for user %s", u.Name)
|
|
||||||
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s.writeJSON(w, newSubscription)
|
return s.writeJSON(w, newSubscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleAccountSubscriptionChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleAccountSubscriptionChange(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
matches := apiAccountSubscriptionSingleRegex.FindStringSubmatch(r.URL.Path)
|
|
||||||
if len(matches) != 2 {
|
|
||||||
return errHTTPInternalErrorInvalidPath
|
|
||||||
}
|
|
||||||
subscriptionID := matches[1]
|
|
||||||
updatedSubscription, err := readJSONWithLimit[user.Subscription](r.Body, jsonBodyBytesLimit, false)
|
updatedSubscription, err := readJSONWithLimit[user.Subscription](r.Body, jsonBodyBytesLimit, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u := v.User()
|
u := v.User()
|
||||||
if u.Prefs == nil || u.Prefs.Subscriptions == nil {
|
prefs := u.Prefs
|
||||||
|
if prefs == nil || prefs.Subscriptions == nil {
|
||||||
return errHTTPNotFound
|
return errHTTPNotFound
|
||||||
}
|
}
|
||||||
prefs := u.Prefs
|
|
||||||
var subscription *user.Subscription
|
var subscription *user.Subscription
|
||||||
for _, sub := range prefs.Subscriptions {
|
for _, sub := range prefs.Subscriptions {
|
||||||
if sub.ID == subscriptionID {
|
if sub.BaseURL == updatedSubscription.BaseURL && sub.Topic == updatedSubscription.Topic {
|
||||||
sub.DisplayName = updatedSubscription.DisplayName
|
sub.DisplayName = updatedSubscription.DisplayName
|
||||||
subscription = sub
|
subscription = sub
|
||||||
break
|
break
|
||||||
|
@ -377,14 +359,7 @@ func (s *Server) handleAccountSubscriptionChange(w http.ResponseWriter, r *http.
|
||||||
if subscription == nil {
|
if subscription == nil {
|
||||||
return errHTTPNotFound
|
return errHTTPNotFound
|
||||||
}
|
}
|
||||||
logvr(v, r).
|
logvr(v, r).Tag(tagAccount).With(subscription).Debug("Changing subscription for user %s", u.Name)
|
||||||
Tag(tagAccount).
|
|
||||||
Fields(log.Context{
|
|
||||||
"base_url": subscription.BaseURL,
|
|
||||||
"topic": subscription.Topic,
|
|
||||||
"display_name": subscription.DisplayName,
|
|
||||||
}).
|
|
||||||
Debug("Changing subscription for user %s", u.Name)
|
|
||||||
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -392,31 +367,23 @@ func (s *Server) handleAccountSubscriptionChange(w http.ResponseWriter, r *http.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleAccountSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleAccountSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
matches := apiAccountSubscriptionSingleRegex.FindStringSubmatch(r.URL.Path)
|
// DELETEs cannot have a body, and we don't want it in the path
|
||||||
if len(matches) != 2 {
|
deleteBaseURL := readParam(r, "X-BaseURL", "BaseURL")
|
||||||
return errHTTPInternalErrorInvalidPath
|
deleteTopic := readParam(r, "X-Topic", "Topic")
|
||||||
}
|
|
||||||
subscriptionID := matches[1]
|
|
||||||
u := v.User()
|
u := v.User()
|
||||||
if u.Prefs == nil || u.Prefs.Subscriptions == nil {
|
prefs := u.Prefs
|
||||||
|
if prefs == nil || prefs.Subscriptions == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
newSubscriptions := make([]*user.Subscription, 0)
|
newSubscriptions := make([]*user.Subscription, 0)
|
||||||
for _, subscription := range u.Prefs.Subscriptions {
|
for _, sub := range u.Prefs.Subscriptions {
|
||||||
if subscription.ID == subscriptionID {
|
if sub.BaseURL == deleteBaseURL && sub.Topic == deleteTopic {
|
||||||
logvr(v, r).
|
logvr(v, r).Tag(tagAccount).With(sub).Debug("Removing subscription for user %s", u.Name)
|
||||||
Tag(tagAccount).
|
|
||||||
Fields(log.Context{
|
|
||||||
"base_url": subscription.BaseURL,
|
|
||||||
"topic": subscription.Topic,
|
|
||||||
}).
|
|
||||||
Debug("Removing subscription for user %s", u.Name)
|
|
||||||
} else {
|
} else {
|
||||||
newSubscriptions = append(newSubscriptions, subscription)
|
newSubscriptions = append(newSubscriptions, sub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(newSubscriptions) < len(u.Prefs.Subscriptions) {
|
if len(newSubscriptions) < len(prefs.Subscriptions) {
|
||||||
prefs := u.Prefs
|
|
||||||
prefs.Subscriptions = newSubscriptions
|
prefs.Subscriptions = newSubscriptions
|
||||||
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
if err := s.userManager.ChangeSettings(u.ID, prefs); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -214,13 +214,11 @@ func TestAccount_Subscription_AddUpdateDelete(t *testing.T) {
|
||||||
require.Equal(t, 200, rr.Code)
|
require.Equal(t, 200, rr.Code)
|
||||||
account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
|
account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
|
||||||
require.Equal(t, 1, len(account.Subscriptions))
|
require.Equal(t, 1, len(account.Subscriptions))
|
||||||
require.NotEmpty(t, account.Subscriptions[0].ID)
|
|
||||||
require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
|
require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
|
||||||
require.Equal(t, "def", account.Subscriptions[0].Topic)
|
require.Equal(t, "def", account.Subscriptions[0].Topic)
|
||||||
require.Nil(t, account.Subscriptions[0].DisplayName)
|
require.Nil(t, account.Subscriptions[0].DisplayName)
|
||||||
|
|
||||||
subscriptionID := account.Subscriptions[0].ID
|
rr = request(t, s, "PATCH", "/v1/account/subscription", `{"base_url": "http://abc.com", "topic": "def", "display_name": "ding dong"}`, map[string]string{
|
||||||
rr = request(t, s, "PATCH", "/v1/account/subscription/"+subscriptionID, `{"display_name": "ding dong"}`, map[string]string{
|
|
||||||
"Authorization": util.BasicAuth("phil", "phil"),
|
"Authorization": util.BasicAuth("phil", "phil"),
|
||||||
})
|
})
|
||||||
require.Equal(t, 200, rr.Code)
|
require.Equal(t, 200, rr.Code)
|
||||||
|
@ -231,13 +229,14 @@ func TestAccount_Subscription_AddUpdateDelete(t *testing.T) {
|
||||||
require.Equal(t, 200, rr.Code)
|
require.Equal(t, 200, rr.Code)
|
||||||
account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
|
account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
|
||||||
require.Equal(t, 1, len(account.Subscriptions))
|
require.Equal(t, 1, len(account.Subscriptions))
|
||||||
require.Equal(t, subscriptionID, account.Subscriptions[0].ID)
|
|
||||||
require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
|
require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
|
||||||
require.Equal(t, "def", account.Subscriptions[0].Topic)
|
require.Equal(t, "def", account.Subscriptions[0].Topic)
|
||||||
require.Equal(t, util.String("ding dong"), account.Subscriptions[0].DisplayName)
|
require.Equal(t, util.String("ding dong"), account.Subscriptions[0].DisplayName)
|
||||||
|
|
||||||
rr = request(t, s, "DELETE", "/v1/account/subscription/"+subscriptionID, "", map[string]string{
|
rr = request(t, s, "DELETE", "/v1/account/subscription", "", map[string]string{
|
||||||
"Authorization": util.BasicAuth("phil", "phil"),
|
"Authorization": util.BasicAuth("phil", "phil"),
|
||||||
|
"X-BaseURL": "http://abc.com",
|
||||||
|
"X-Topic": "def",
|
||||||
})
|
})
|
||||||
require.Equal(t, 200, rr.Code)
|
require.Equal(t, 200, rr.Code)
|
||||||
|
|
||||||
|
|
|
@ -726,7 +726,6 @@ func TestManager_ChangeSettings(t *testing.T) {
|
||||||
},
|
},
|
||||||
Subscriptions: []*Subscription{
|
Subscriptions: []*Subscription{
|
||||||
{
|
{
|
||||||
ID: "someID",
|
|
||||||
BaseURL: "https://ntfy.sh",
|
BaseURL: "https://ntfy.sh",
|
||||||
Topic: "mytopic",
|
Topic: "mytopic",
|
||||||
DisplayName: util.String("My Topic"),
|
DisplayName: util.String("My Topic"),
|
||||||
|
@ -742,7 +741,6 @@ func TestManager_ChangeSettings(t *testing.T) {
|
||||||
require.Equal(t, util.String("ding"), u.Prefs.Notification.Sound)
|
require.Equal(t, util.String("ding"), u.Prefs.Notification.Sound)
|
||||||
require.Equal(t, util.Int(2), u.Prefs.Notification.MinPriority)
|
require.Equal(t, util.Int(2), u.Prefs.Notification.MinPriority)
|
||||||
require.Nil(t, u.Prefs.Notification.DeleteAfter)
|
require.Nil(t, u.Prefs.Notification.DeleteAfter)
|
||||||
require.Equal(t, "someID", u.Prefs.Subscriptions[0].ID)
|
|
||||||
require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
|
require.Equal(t, "https://ntfy.sh", u.Prefs.Subscriptions[0].BaseURL)
|
||||||
require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
|
require.Equal(t, "mytopic", u.Prefs.Subscriptions[0].Topic)
|
||||||
require.Equal(t, util.String("My Topic"), u.Prefs.Subscriptions[0].DisplayName)
|
require.Equal(t, util.String("My Topic"), u.Prefs.Subscriptions[0].DisplayName)
|
||||||
|
|
|
@ -105,12 +105,19 @@ func (t *Tier) Context() log.Context {
|
||||||
|
|
||||||
// Subscription represents a user's topic subscription
|
// Subscription represents a user's topic subscription
|
||||||
type Subscription struct {
|
type Subscription struct {
|
||||||
ID string `json:"id"`
|
|
||||||
BaseURL string `json:"base_url"`
|
BaseURL string `json:"base_url"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
DisplayName *string `json:"display_name"`
|
DisplayName *string `json:"display_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context returns fields for the log
|
||||||
|
func (s *Subscription) Context() log.Context {
|
||||||
|
return log.Context{
|
||||||
|
"base_url": s.BaseURL,
|
||||||
|
"topic": s.Topic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NotificationPrefs represents the user's notification settings
|
// NotificationPrefs represents the user's notification settings
|
||||||
type NotificationPrefs struct {
|
type NotificationPrefs struct {
|
||||||
Sound *string `json:"sound,omitempty"`
|
Sound *string `json:"sound,omitempty"`
|
||||||
|
|
|
@ -173,9 +173,12 @@ class AccountApi {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async addSubscription(payload) {
|
async addSubscription(baseUrl, topic) {
|
||||||
const url = accountSubscriptionUrl(config.base_url);
|
const url = accountSubscriptionUrl(config.base_url);
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify({
|
||||||
|
base_url: baseUrl,
|
||||||
|
topic: topic
|
||||||
|
});
|
||||||
console.log(`[AccountApi] Adding user subscription ${url}: ${body}`);
|
console.log(`[AccountApi] Adding user subscription ${url}: ${body}`);
|
||||||
const response = await fetchOrThrow(url, {
|
const response = await fetchOrThrow(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -187,9 +190,13 @@ class AccountApi {
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSubscription(remoteId, payload) {
|
async updateSubscription(baseUrl, topic, payload) {
|
||||||
const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
|
const url = accountSubscriptionUrl(config.base_url);
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify({
|
||||||
|
base_url: baseUrl,
|
||||||
|
topic: topic,
|
||||||
|
...payload
|
||||||
|
});
|
||||||
console.log(`[AccountApi] Updating user subscription ${url}: ${body}`);
|
console.log(`[AccountApi] Updating user subscription ${url}: ${body}`);
|
||||||
const response = await fetchOrThrow(url, {
|
const response = await fetchOrThrow(url, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
@ -201,12 +208,16 @@ class AccountApi {
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSubscription(remoteId) {
|
async deleteSubscription(baseUrl, topic) {
|
||||||
const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
|
const url = accountSubscriptionUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Removing user subscription ${url}`);
|
console.log(`[AccountApi] Removing user subscription ${url}`);
|
||||||
|
const headers = {
|
||||||
|
"X-BaseURL": baseUrl,
|
||||||
|
"X-Topic": topic,
|
||||||
|
}
|
||||||
await fetchOrThrow(url, {
|
await fetchOrThrow(url, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: withBearerAuth({}, session.token())
|
headers: withBearerAuth(headers, session.token()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ class SubscriptionManager {
|
||||||
topic: topic,
|
topic: topic,
|
||||||
mutedUntil: 0,
|
mutedUntil: 0,
|
||||||
last: null,
|
last: null,
|
||||||
remoteId: null,
|
|
||||||
internal: internal || false
|
internal: internal || false
|
||||||
};
|
};
|
||||||
await db.subscriptions.put(subscription);
|
await db.subscriptions.put(subscription);
|
||||||
|
@ -40,24 +39,23 @@ class SubscriptionManager {
|
||||||
console.log(`[SubscriptionManager] Syncing subscriptions from remote`, remoteSubscriptions);
|
console.log(`[SubscriptionManager] Syncing subscriptions from remote`, remoteSubscriptions);
|
||||||
|
|
||||||
// Add remote subscriptions
|
// Add remote subscriptions
|
||||||
let remoteIds = [];
|
let remoteIds = []; // = topicUrl(baseUrl, topic)
|
||||||
for (let i = 0; i < remoteSubscriptions.length; i++) {
|
for (let i = 0; i < remoteSubscriptions.length; i++) {
|
||||||
const remote = remoteSubscriptions[i];
|
const remote = remoteSubscriptions[i];
|
||||||
const local = await this.add(remote.base_url, remote.topic);
|
const local = await this.add(remote.base_url, remote.topic, false);
|
||||||
const reservation = remoteReservations?.find(r => remote.base_url === config.base_url && remote.topic === r.topic) || null;
|
const reservation = remoteReservations?.find(r => remote.base_url === config.base_url && remote.topic === r.topic) || null;
|
||||||
await this.update(local.id, {
|
await this.update(local.id, {
|
||||||
remoteId: remote.id,
|
|
||||||
displayName: remote.display_name, // May be undefined
|
displayName: remote.display_name, // May be undefined
|
||||||
reservation: reservation // May be null!
|
reservation: reservation // May be null!
|
||||||
});
|
});
|
||||||
remoteIds.push(remote.id);
|
remoteIds.push(local.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove local subscriptions that do not exist remotely
|
// Remove local subscriptions that do not exist remotely
|
||||||
const localSubscriptions = await db.subscriptions.toArray();
|
const localSubscriptions = await db.subscriptions.toArray();
|
||||||
for (let i = 0; i < localSubscriptions.length; i++) {
|
for (let i = 0; i < localSubscriptions.length; i++) {
|
||||||
const local = localSubscriptions[i];
|
const local = localSubscriptions[i];
|
||||||
const remoteExists = local.remoteId && remoteIds.includes(local.remoteId);
|
const remoteExists = remoteIds.includes(local.id);
|
||||||
if (!local.internal && !remoteExists) {
|
if (!local.internal && !remoteExists) {
|
||||||
await this.remove(local.id);
|
await this.remove(local.id);
|
||||||
}
|
}
|
||||||
|
@ -174,12 +172,6 @@ class SubscriptionManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRemoteId(subscriptionId, remoteId) {
|
|
||||||
await db.subscriptions.update(subscriptionId, {
|
|
||||||
remoteId: remoteId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async setReservation(subscriptionId, reservation) {
|
async setReservation(subscriptionId, reservation) {
|
||||||
await db.subscriptions.update(subscriptionId, {
|
await db.subscriptions.update(subscriptionId, {
|
||||||
reservation: reservation
|
reservation: reservation
|
||||||
|
|
|
@ -23,7 +23,6 @@ export const accountPasswordUrl = (baseUrl) => `${baseUrl}/v1/account/password`;
|
||||||
export const accountTokenUrl = (baseUrl) => `${baseUrl}/v1/account/token`;
|
export const accountTokenUrl = (baseUrl) => `${baseUrl}/v1/account/token`;
|
||||||
export const accountSettingsUrl = (baseUrl) => `${baseUrl}/v1/account/settings`;
|
export const accountSettingsUrl = (baseUrl) => `${baseUrl}/v1/account/settings`;
|
||||||
export const accountSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/subscription`;
|
export const accountSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/subscription`;
|
||||||
export const accountSubscriptionSingleUrl = (baseUrl, id) => `${baseUrl}/v1/account/subscription/${id}`;
|
|
||||||
export const accountReservationUrl = (baseUrl) => `${baseUrl}/v1/account/reservation`;
|
export const accountReservationUrl = (baseUrl) => `${baseUrl}/v1/account/reservation`;
|
||||||
export const accountReservationSingleUrl = (baseUrl, topic) => `${baseUrl}/v1/account/reservation/${topic}`;
|
export const accountReservationSingleUrl = (baseUrl, topic) => `${baseUrl}/v1/account/reservation/${topic}`;
|
||||||
export const accountBillingSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/billing/subscription`;
|
export const accountBillingSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/billing/subscription`;
|
||||||
|
|
|
@ -299,11 +299,7 @@ export const subscribeTopic = async (baseUrl, topic) => {
|
||||||
const subscription = await subscriptionManager.add(baseUrl, topic);
|
const subscription = await subscriptionManager.add(baseUrl, topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
try {
|
try {
|
||||||
const remoteSubscription = await accountApi.addSubscription({
|
await accountApi.addSubscription(baseUrl, topic);
|
||||||
base_url: baseUrl,
|
|
||||||
topic: topic
|
|
||||||
});
|
|
||||||
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[SubscribeDialog] Subscribing to topic ${topic} failed`, e);
|
console.log(`[SubscribeDialog] Subscribing to topic ${topic} failed`, e);
|
||||||
if (e instanceof UnauthorizedError) {
|
if (e instanceof UnauthorizedError) {
|
||||||
|
|
|
@ -109,9 +109,9 @@ export const SubscriptionPopup = (props) => {
|
||||||
const handleUnsubscribe = async () => {
|
const handleUnsubscribe = async () => {
|
||||||
console.log(`[SubscriptionPopup] Unsubscribing from ${props.subscription.id}`, props.subscription);
|
console.log(`[SubscriptionPopup] Unsubscribing from ${props.subscription.id}`, props.subscription);
|
||||||
await subscriptionManager.remove(props.subscription.id);
|
await subscriptionManager.remove(props.subscription.id);
|
||||||
if (session.exists() && props.subscription.remoteId) {
|
if (session.exists() && !subscription.internal) {
|
||||||
try {
|
try {
|
||||||
await accountApi.deleteSubscription(props.subscription.remoteId);
|
await accountApi.deleteSubscription(props.subscription.baseUrl, props.subscription.topic);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[SubscriptionPopup] Error unsubscribing`, e);
|
console.log(`[SubscriptionPopup] Error unsubscribing`, e);
|
||||||
if (e instanceof UnauthorizedError) {
|
if (e instanceof UnauthorizedError) {
|
||||||
|
@ -198,10 +198,10 @@ const DisplayNameDialog = (props) => {
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
await subscriptionManager.setDisplayName(subscription.id, displayName);
|
await subscriptionManager.setDisplayName(subscription.id, displayName);
|
||||||
if (session.exists() && subscription.remoteId) {
|
if (session.exists() && !subscription.internal) {
|
||||||
try {
|
try {
|
||||||
console.log(`[SubscriptionSettingsDialog] Updating subscription display name to ${displayName}`);
|
console.log(`[SubscriptionSettingsDialog] Updating subscription display name to ${displayName}`);
|
||||||
await accountApi.updateSubscription(subscription.remoteId, { display_name: displayName });
|
await accountApi.updateSubscription(subscription.baseUrl, subscription.topic, { display_name: displayName });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[SubscriptionSettingsDialog] Error updating subscription`, e);
|
console.log(`[SubscriptionSettingsDialog] Error updating subscription`, e);
|
||||||
if (e instanceof UnauthorizedError) {
|
if (e instanceof UnauthorizedError) {
|
||||||
|
|
|
@ -100,11 +100,7 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
||||||
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
try {
|
try {
|
||||||
const remoteSubscription = await accountApi.addSubscription({
|
await accountApi.addSubscription(baseUrl, params.topic);
|
||||||
base_url: baseUrl,
|
|
||||||
topic: params.topic
|
|
||||||
});
|
|
||||||
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Hooks] Auto-subscribing failed`, e);
|
console.log(`[Hooks] Auto-subscribing failed`, e);
|
||||||
if (e instanceof UnauthorizedError) {
|
if (e instanceof UnauthorizedError) {
|
||||||
|
|
Loading…
Reference in a new issue