mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
support registry listing option
This commit is contained in:
parent
318ea81e7e
commit
bf21af0a7d
18
doc/api.oa3
18
doc/api.oa3
@ -390,8 +390,13 @@ paths:
|
||||
- account
|
||||
description: Get disabled status of account. Authorized to account username and password.
|
||||
operationId: get-account-status
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: agent
|
||||
in: query
|
||||
description: agent token
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
@ -410,8 +415,13 @@ paths:
|
||||
- account
|
||||
description: Set whether account is publicly listed.
|
||||
operationId: set-account-seachable
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: agent
|
||||
in: query
|
||||
description: agent token
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'201':
|
||||
description: success
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
func GetAccountStatus(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
account, code, err := BearerAppToken(r, false)
|
||||
account, code, err := ParamAgentToken(r, true);
|
||||
if err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
|
@ -2,12 +2,13 @@ package databag
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"gorm.io/gorm"
|
||||
"databag/internal/store"
|
||||
)
|
||||
|
||||
func SetAccountSearchable(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
account, code, err := BearerAppToken(r, false)
|
||||
account, code, err := ParamAgentToken(r, true);
|
||||
if err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
@ -19,11 +20,22 @@ func SetAccountSearchable(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = store.DB.Model(account).Update("searchable", flag).Error; err != nil {
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Model(account).Update("searchable", flag).Error; res != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, res)
|
||||
return res
|
||||
}
|
||||
if res := tx.Model(&account).Update("account_revision", account.AccountRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
SetStatus(account)
|
||||
WriteResponse(w, nil)
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,7 @@ func Status(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func getRevision(account *store.Account) Revision {
|
||||
var r Revision
|
||||
r.Account = account.AccountRevision
|
||||
r.Profile = account.ProfileRevision
|
||||
r.Article = account.ArticleRevision
|
||||
r.Channel = account.ChannelRevision
|
||||
|
@ -361,7 +361,9 @@ type ProfileData struct {
|
||||
|
||||
type Revision struct {
|
||||
|
||||
Profile int64 `json:"profile"`
|
||||
Account int64 `json:"account"`
|
||||
|
||||
Profile int64 `json:"profile"`
|
||||
|
||||
Article int64 `json:"article"`
|
||||
|
||||
|
@ -60,6 +60,7 @@ type Account struct {
|
||||
Guid string `gorm:"not null;uniqueIndex"`
|
||||
Username string `gorm:"not null;uniqueIndex"`
|
||||
Password []byte `gorm:"not null"`
|
||||
AccountRevision int64 `gorm:"not null;default:1"`
|
||||
ProfileRevision int64 `gorm:"not null;default:1"`
|
||||
ArticleRevision int64 `gorm:"not null;default:1"`
|
||||
GroupRevision int64 `gorm:"not null;default:1"`
|
||||
|
@ -40,6 +40,17 @@ export async function setLogin(username, password) {
|
||||
return await login.json()
|
||||
}
|
||||
|
||||
export async function setAccountSearchable(token, flag) {
|
||||
let res = await fetchWithTimeout('/account/searchable?agent=' + token, { method: 'PUT', body: JSON.stringify(flag), timeout: FETCH_TIMEOUT })
|
||||
checkResponse(res);
|
||||
}
|
||||
|
||||
export async function getAccountStatus(token) {
|
||||
let status = await fetchWithTimeout('/account/status?agent=' + token, { method: 'GET', timeout: FETCH_TIMEOUT });
|
||||
checkResponse(status);
|
||||
return await status.json()
|
||||
}
|
||||
|
||||
export async function createAccount(username, password) {
|
||||
let headers = new Headers()
|
||||
headers.append('Credentials', 'Basic ' + base64.encode(username + ":" + password));
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { setProfileImage, setProfileData, getProfileImageUrl, getProfile, getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
|
||||
import { setProfileImage, setProfileData, getProfileImageUrl, getAccountStatus, setAccountSearchable, getProfile, getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
|
||||
|
||||
async function updateAccount(token, updateData) {
|
||||
let status = await getAccountStatus(token);
|
||||
console.log(status);
|
||||
updateData({ status: status });
|
||||
}
|
||||
|
||||
async function updateProfile(token, updateData) {
|
||||
let profile = await getProfile(token);
|
||||
@ -41,10 +47,10 @@ export function useAppContext() {
|
||||
const [state, setState] = useState(null);
|
||||
|
||||
const groupRevision = useRef(null);
|
||||
const groups = useRef(new Map());
|
||||
|
||||
const accountRevision = useRef(null);
|
||||
const profileRevision = useRef(null);
|
||||
const profile = useRef({});
|
||||
|
||||
const groups = useRef(new Map());
|
||||
|
||||
const ws = useRef(null);
|
||||
const revision = useRef(null);
|
||||
@ -60,10 +66,10 @@ export function useAppContext() {
|
||||
|
||||
const resetData = () => {
|
||||
revision.current = null;
|
||||
profile.current = {};
|
||||
profileRevision.current = null;
|
||||
groups.current = new Map();
|
||||
groupRevision.current = null;
|
||||
groups.current = new Map();
|
||||
setState(null);
|
||||
}
|
||||
|
||||
const userActions = {
|
||||
@ -77,6 +83,9 @@ export function useAppContext() {
|
||||
setProfileImage: async (image) => {
|
||||
await setProfileImage(state.token, image);
|
||||
},
|
||||
setAccountSearchable: async (flag) => {
|
||||
await setAccountSearchable(state.token, flag);
|
||||
},
|
||||
profileImageUrl: () => getProfileImageUrl(state.token, state.Data?.profile?.revision)
|
||||
}
|
||||
|
||||
@ -114,6 +123,12 @@ export function useAppContext() {
|
||||
profileRevision.current = rev.profile
|
||||
}
|
||||
|
||||
// update account status if revision changed
|
||||
if (rev.account != accountRevision.current) {
|
||||
await updateAccount(token, updateData)
|
||||
accountRevision.current = rev.account
|
||||
}
|
||||
|
||||
// check if new revision was received during processing
|
||||
if (rev == revision.current) {
|
||||
revision.current = null
|
||||
@ -165,7 +180,6 @@ export function useAppContext() {
|
||||
const session = JSON.parse(storage)
|
||||
if (session?.access === 'admin') {
|
||||
setState({ token: session.token, access: session.access })
|
||||
setWebsocket(session.token);
|
||||
} else if (session?.access === 'user') {
|
||||
setState({ token: session.token, access: session.access })
|
||||
setWebsocket(session.token);
|
||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'
|
||||
import { EditIcon, ProfileWrapper, CloseButton, ModalFooter, SelectButton } from './Profile.styled';
|
||||
import { UserOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { useProfile } from './useProfile.hook';
|
||||
import { Button, Modal } from 'antd'
|
||||
import { Button, Checkbox, Modal } from 'antd'
|
||||
import { ProfileInfo } from './ProfileInfo/ProfileInfo';
|
||||
import { ProfileImage } from './ProfileImage/ProfileImage';
|
||||
|
||||
@ -68,6 +68,10 @@ export function Profile(props) {
|
||||
reader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
|
||||
const onSearchable = (flag) => {
|
||||
actions.setSearchable(flag);
|
||||
}
|
||||
|
||||
const Footer = (
|
||||
<ModalFooter>
|
||||
<input type='file' id='file' accept="image/*" ref={imageFile} onChange={e => selected(e)} style={{display: 'none'}}/>
|
||||
@ -87,6 +91,10 @@ export function Profile(props) {
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="profile">
|
||||
<div class="registry">
|
||||
<span class="search">Listed in Registry</span>
|
||||
<Checkbox checked={state.searchable} onChange={(e) => onSearchable(e.target.checked)} />
|
||||
</div>
|
||||
<div class="avatar" onClick={() => setLogoVisible(true)}>
|
||||
<Logo />
|
||||
<div class="logoedit">
|
||||
|
@ -56,6 +56,16 @@ export const ProfileWrapper = styled.div`
|
||||
flex: 3
|
||||
}
|
||||
|
||||
.registry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -10,6 +10,7 @@ export function useProfile() {
|
||||
description: '',
|
||||
location: '',
|
||||
imageUrl: null,
|
||||
searchable: false,
|
||||
modalBusy: false,
|
||||
modalName: '',
|
||||
modalLocation: '',
|
||||
@ -59,6 +60,14 @@ export function useProfile() {
|
||||
}
|
||||
return set
|
||||
},
|
||||
setSearchable: async (flag) => {
|
||||
try {
|
||||
await app.actions.setAccountSearchable(flag);
|
||||
}
|
||||
catch (err) {
|
||||
window.alert(err);
|
||||
}
|
||||
},
|
||||
setProfileImage: async () => {
|
||||
let set = false
|
||||
if(!state.modalBusy) {
|
||||
@ -70,10 +79,10 @@ export function useProfile() {
|
||||
img.onload = () => {
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext('2d');
|
||||
canvas.width = state.crop.w;
|
||||
canvas.height = state.crop.h;
|
||||
canvas.width = 256;
|
||||
canvas.height = 256;
|
||||
context.drawImage(img, state.crop.x, state.crop.y, state.crop.w, state.crop.h,
|
||||
0, 0, state.crop.w, state.crop.h);
|
||||
0, 0, 256, 256);
|
||||
resolve(canvas.toDataURL());
|
||||
}
|
||||
img.onerror = reject;
|
||||
@ -112,6 +121,10 @@ export function useProfile() {
|
||||
updateState({ location: profile.location });
|
||||
updateState({ modalLocation: profile.location });
|
||||
}
|
||||
if (app?.state?.Data?.status) {
|
||||
let status = app.state.Data.status;
|
||||
updateState({ searchable: status.searchable });
|
||||
}
|
||||
}, [app])
|
||||
|
||||
return { state, actions };
|
||||
|
@ -29,6 +29,8 @@ export const IdentityWrapper = styled.div`
|
||||
}
|
||||
|
||||
.username {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
Loading…
Reference in New Issue
Block a user