diff --git a/doc/api.oa3 b/doc/api.oa3
index e2e431cb..a83c6fa1 100644
--- a/doc/api.oa3
+++ b/doc/api.oa3
@@ -363,7 +363,33 @@ paths:
description: permission denied
'500':
description: internal server error
+
+ /account/listing/{guid}/message:
+ get:
+ tags:
+ - account
+ description: Get profile message of searchable account. Endpoint is publically accessible.
+ operationId: get-account-listing-message
+ parameters:
+ - name: guid
+ in: path
+ description: filter for specified guid
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataMessage'
+ '401':
+ description: permission denied
+ '500':
+ description: internal server error
+
/account/token:
get:
tags:
diff --git a/net/server/internal/api_addCard.go b/net/server/internal/api_addCard.go
index 856aa5e5..60616c32 100644
--- a/net/server/internal/api_addCard.go
+++ b/net/server/internal/api_addCard.go
@@ -12,7 +12,7 @@ import (
func AddCard(w http.ResponseWriter, r *http.Request) {
- account, code, err := BearerAppToken(r, false)
+ account, code, err := ParamAgentToken(r, false)
if err != nil {
ErrResponse(w, code, err)
return
@@ -61,53 +61,27 @@ func AddCard(w http.ResponseWriter, r *http.Request) {
AccountID: account.Guid,
}
- // create new card or update existing
- if err = store.DB.Where("account_id = ? AND card_id = 0", account.ID).First(slot).Error; err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- ErrResponse(w, http.StatusInternalServerError, err)
- return
+ // save new card
+ err = store.DB.Transaction(func(tx *gorm.DB) error {
+ if res := tx.Save(card).Error; res != nil {
+ return res
}
- err = store.DB.Transaction(func(tx *gorm.DB) error {
- if res := tx.Save(card).Error; res != nil {
- return res
- }
- slot.CardSlotId = uuid.New().String()
- slot.AccountID = account.ID
- slot.Revision = account.CardRevision + 1
- slot.CardID = card.ID
- slot.Card = card
- if res := tx.Save(slot).Error; res != nil {
- return res
- }
- if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
- return res
- }
- return nil
- })
- if err != nil {
- ErrResponse(w, http.StatusInternalServerError, err)
- return
+ slot.CardSlotId = uuid.New().String()
+ slot.AccountID = account.ID
+ slot.Revision = account.CardRevision + 1
+ slot.CardID = card.ID
+ slot.Card = card
+ if res := tx.Save(slot).Error; res != nil {
+ return res
}
- } else {
- err = store.DB.Transaction(func(tx *gorm.DB) error {
- if res := tx.Save(&card).Error; res != nil {
- return res
- }
- slot.Revision = account.CardRevision + 1
- slot.CardID = card.ID
- slot.Card = card
- if res := tx.Save(&slot).Error; res != nil {
- return res
- }
- if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
- return res
- }
- return nil
- })
- if err != nil {
- ErrResponse(w, http.StatusInternalServerError, err)
- return
+ if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
+ return res
}
+ return nil
+ })
+ if err != nil {
+ ErrResponse(w, http.StatusInternalServerError, err)
+ return
}
} else {
diff --git a/net/server/internal/api_removeCard.go b/net/server/internal/api_removeCard.go
index c33259b6..932ef13b 100644
--- a/net/server/internal/api_removeCard.go
+++ b/net/server/internal/api_removeCard.go
@@ -10,7 +10,7 @@ import (
func RemoveCard(w http.ResponseWriter, r *http.Request) {
- account, code, err := BearerAppToken(r, false);
+ account, code, err := ParamAgentToken(r, false);
if err != nil {
ErrResponse(w, code, err)
return
diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go
index b881f534..eeeb3b61 100644
--- a/net/server/internal/routers.go
+++ b/net/server/internal/routers.go
@@ -104,6 +104,13 @@ var routes = Routes{
GetAccountListingImage,
},
+ Route{
+ "GetAccountListingMessage",
+ strings.ToUpper("Get"),
+ "/account/listing/{guid}/message",
+ GetAccountListingMessage,
+ },
+
Route{
"GetAccountStatus",
strings.ToUpper("Get"),
diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go
index c1d17719..a60b3195 100644
--- a/net/server/internal/store/schema.go
+++ b/net/server/internal/store/schema.go
@@ -148,7 +148,6 @@ type Card struct {
Location string
Image string
Version string `gorm:"not null"`
- Revision string `gorm:"not null"`
Node string `gorm:"not null"`
ProfileRevision int64 `gorm:"not null"`
DetailRevision int64 `gorm:"not null;default:1"`
diff --git a/net/server/internal/testUtil.go b/net/server/internal/testUtil.go
index 57b98201..965b9ed5 100644
--- a/net/server/internal/testUtil.go
+++ b/net/server/internal/testUtil.go
@@ -586,10 +586,9 @@ func AddTestCard(account string, contact string) (cardId string, err error) {
}
// add A card in B
- if r, w, err = NewRequest("POST", "/contact/cards", &msg); err != nil {
+ if r, w, err = NewRequest("POST", "/contact/cards?agent=" + account, &msg); err != nil {
return
}
- SetBearerAuth(r, account)
AddCard(w, r)
if err = ReadResponse(w, &card); err != nil {
return
diff --git a/net/web/src/Api/addCard.js b/net/web/src/Api/addCard.js
new file mode 100644
index 00000000..8109c94b
--- /dev/null
+++ b/net/web/src/Api/addCard.js
@@ -0,0 +1,8 @@
+import { checkResponse, fetchWithTimeout } from './fetchUtil';
+
+export async function addCard(token, message) {
+ let card = await fetchWithTimeout(`/contact/cards?agent=${token}`, { method: 'POST', body: JSON.stringify(message)} );
+ checkResponse(card);
+ return await card.json();
+}
+
diff --git a/net/web/src/Api/fetchUtil.js b/net/web/src/Api/fetchUtil.js
new file mode 100644
index 00000000..6ea98813
--- /dev/null
+++ b/net/web/src/Api/fetchUtil.js
@@ -0,0 +1,17 @@
+const TIMEOUT = 15000;
+
+//await new Promise(r => setTimeout(r, 2000));
+
+export function checkResponse(response) {
+ if(response.status >= 400 && response.status < 600) {
+ throw new Error(response.url + " failed");
+ }
+}
+
+export async function fetchWithTimeout(url, options) {
+ return Promise.race([
+ fetch(url, options).catch(err => { throw new Error(url + ' failed'); }),
+ new Promise((_, reject) => setTimeout(() => reject(new Error(url + ' timeout')), TIMEOUT))
+ ]);
+}
+
diff --git a/net/web/src/Api/getListingMessage.js b/net/web/src/Api/getListingMessage.js
new file mode 100644
index 00000000..aebad94b
--- /dev/null
+++ b/net/web/src/Api/getListingMessage.js
@@ -0,0 +1,8 @@
+import { checkResponse, fetchWithTimeout } from './fetchUtil';
+
+export async function getListingMessage(server, guid) {
+ let listing = await fetchWithTimeout(`https://${server}/account/listing/${guid}/message`, { method: 'GET' });
+ checkResponse(listing);
+ return await listing.json();
+}
+
diff --git a/net/web/src/Api/removeCard.js b/net/web/src/Api/removeCard.js
new file mode 100644
index 00000000..3effc5c8
--- /dev/null
+++ b/net/web/src/Api/removeCard.js
@@ -0,0 +1,8 @@
+import { checkResponse, fetchWithTimeout } from './fetchUtil';
+
+export async function removeCard(token, cardId) {
+ let card = await fetchWithTimeout(`/contact/cards/${cardId}?agent=${token}`, { method: 'DELETE' } );
+ checkResponse(card);
+ return await card.json();
+}
+
diff --git a/net/web/src/AppContext/useAppContext.hook.js b/net/web/src/AppContext/useAppContext.hook.js
index af372a50..0e1d3cd2 100644
--- a/net/web/src/AppContext/useAppContext.hook.js
+++ b/net/web/src/AppContext/useAppContext.hook.js
@@ -143,6 +143,7 @@ export function useAppContext() {
const resetData = () => {
revision.current = null;
+ accountRevision.current = null;
profileRevision.current = null;
groupRevision.current = null;
cardRevision.current = null;
diff --git a/net/web/src/User/Contact/Contact.jsx b/net/web/src/User/Contact/Contact.jsx
index b3896d1b..70e42f3f 100644
--- a/net/web/src/User/Contact/Contact.jsx
+++ b/net/web/src/User/Contact/Contact.jsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'
import { CloseOutlined, UserOutlined } from '@ant-design/icons';
import { useContact } from './useContact.hook';
import { Button, Checkbox, Modal } from 'antd'
-import { ContactWrapper, ProfileButton, CloseButton } from './Contact.styled';
+import { ContactWrapper, ProfileButton, CloseButton, ContactSpin } from './Contact.styled';
export function Contact() {
@@ -48,7 +48,7 @@ export function Contact() {
const Remove = () => {
if (state.showButtons.remove) {
- return