changing default asset path

This commit is contained in:
Roland Osborne 2022-03-10 23:57:27 -08:00
parent 54b9737cd6
commit 7e824d19b3
13 changed files with 123 additions and 45 deletions

View File

@ -273,13 +273,6 @@ paths:
- account - account
description: Check if any public accounts are available description: Check if any public accounts are available
operationId: get-account-available operationId: get-account-available
parameters:
- name: public
in: query
description: for open access accounts
required: false
schema:
type: boolean
responses: responses:
'200': '200':
description: available public accounts description: available public accounts

View File

@ -2,6 +2,7 @@ package databag
import ( import (
"os" "os"
"errors"
"net/http" "net/http"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
@ -10,11 +11,23 @@ import (
) )
func AddAccount(w http.ResponseWriter, r *http.Request) { func AddAccount(w http.ResponseWriter, r *http.Request) {
var token *store.AccountToken
token, res := BearerAccountToken(r); if r.Header.Get("Authorization") == "" {
if res != nil || token.TokenType != APP_TOKENCREATE { if available, err := getAvailableAccounts(); err != nil {
ErrResponse(w, http.StatusUnauthorized, res) ErrResponse(w, http.StatusInternalServerError, err)
return return
} else if available == 0 {
ErrResponse(w, http.StatusForbidden, errors.New("no open accounts available"))
return
}
} else {
var err error
token, err = BearerAccountToken(r);
if err != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
} }
username, password, ret := BasicCredentials(r); username, password, ret := BasicCredentials(r);
@ -23,6 +36,17 @@ func AddAccount(w http.ResponseWriter, r *http.Request) {
return return
} }
// check if username is taken
var count int64
if err := store.DB.Model(&store.Account{}).Where("username = ?", username).Count(&count).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err);
return
}
if count != 0 {
ErrResponse(w, http.StatusConflict, errors.New("username already taken"))
return
}
// generate account key // generate account key
privateKey, publicKey, keyType, err := GenerateRsaKeyPair() privateKey, publicKey, keyType, err := GenerateRsaKeyPair()
if err != nil { if err != nil {
@ -42,7 +66,7 @@ func AddAccount(w http.ResponseWriter, r *http.Request) {
fingerprint := hex.EncodeToString(hash[:]) fingerprint := hex.EncodeToString(hash[:])
// create path for account data // create path for account data
path := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + fingerprint path := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + fingerprint
if err := os.Mkdir(path, os.ModePerm); err != nil { if err := os.Mkdir(path, os.ModePerm); err != nil {
ErrResponse(w, http.StatusInternalServerError, err) ErrResponse(w, http.StatusInternalServerError, err)
} }
@ -68,8 +92,10 @@ func AddAccount(w http.ResponseWriter, r *http.Request) {
if res := tx.Create(&account).Error; res != nil { if res := tx.Create(&account).Error; res != nil {
return res; return res;
} }
if res := tx.Delete(token).Error; res != nil { if token != nil {
return res; if res := tx.Delete(token).Error; res != nil {
return res;
}
} }
return nil; return nil;
}); });

View File

@ -70,7 +70,7 @@ func AddChannelTopicAsset(w http.ResponseWriter, r *http.Request) {
// save new file // save new file
id := uuid.New().String() id := uuid.New().String()
path := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + channelSlot.Account.Guid + "/" + id path := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + channelSlot.Account.Guid + "/" + id
if err := r.ParseMultipartForm(32 << 20); err != nil { if err := r.ParseMultipartForm(32 << 20); err != nil {
ErrResponse(w, http.StatusBadRequest, err) ErrResponse(w, http.StatusBadRequest, err)
return return

View File

@ -7,21 +7,28 @@ import (
func GetAccountAvailable(w http.ResponseWriter, r *http.Request) { func GetAccountAvailable(w http.ResponseWriter, r *http.Request) {
public := r.FormValue("public") == "true" available, err := getAvailableAccounts()
open := getBoolConfigValue(CONFIG_OPENACCESS, true) if err != nil {
limit := getNumConfigValue(CONFIG_ACCOUNTLIMIT, 16)
var count int64
if err := store.DB.Model(&store.Account{}).Count(&count).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err) ErrResponse(w, http.StatusInternalServerError, err)
return return
} }
var available int64
if (!public || open) && limit > count {
available = limit - count
}
WriteResponse(w, &available) WriteResponse(w, &available)
} }
func getAvailableAccounts() (available int64, err error) {
open := getBoolConfigValue(CONFIG_OPENACCESS, true)
limit := getNumConfigValue(CONFIG_ACCOUNTLIMIT, 16)
var count int64
if err = store.DB.Model(&store.Account{}).Count(&count).Error; err != nil {
return
}
if open && limit > count {
available = limit - count
}
return
}

View File

@ -1,6 +1,7 @@
package databag package databag
import ( import (
"errors"
"net/http" "net/http"
"databag/internal/store" "databag/internal/store"
) )
@ -10,23 +11,33 @@ type accountUsername struct {
} }
func GetAccountUsername(w http.ResponseWriter, r *http.Request) { func GetAccountUsername(w http.ResponseWriter, r *http.Request) {
var token *store.AccountToken
token, err := BearerAccountToken(r); if r.Header.Get("Authorization") == "" {
if err != nil || (token.TokenType != "create" && token.TokenType != "reset") { if available, err := getAvailableAccounts(); err != nil {
LogMsg("invalid token") ErrResponse(w, http.StatusInternalServerError, err)
w.WriteHeader(http.StatusUnauthorized) return
return } else if available == 0 {
ErrResponse(w, http.StatusUnauthorized, errors.New("no open accounts available"))
return
}
} else {
var err error
token, err = BearerAccountToken(r);
if err != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
} }
username := r.URL.Query().Get("username") username := r.URL.Query().Get("username")
if username == "" { if username == "" {
LogMsg("invalid username") ErrResponse(w, http.StatusBadRequest, errors.New("specify a username"))
w.WriteHeader(http.StatusBadRequest)
return return
} }
var accounts []accountUsername; var accounts []accountUsername;
err = store.DB.Model(&store.Account{}).Where("username = ?", username).Find(&accounts).Error err := store.DB.Model(&store.Account{}).Where("username = ?", username).Find(&accounts).Error
if err != nil { if err != nil {
LogMsg("failed to query accounts") LogMsg("failed to query accounts")
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

View File

@ -42,7 +42,7 @@ func GetChannelTopicAsset(w http.ResponseWriter, r *http.Request) {
} }
// construct file path // construct file path
path := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + act.Guid + "/" + asset.AssetId path := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + act.Guid + "/" + asset.AssetId
http.ServeFile(w, r, path) http.ServeFile(w, r, path)
} }

View File

@ -78,7 +78,7 @@ func RemoveAccount(w http.ResponseWriter, r *http.Request) {
} }
// delete asset files // delete asset files
path := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + account.Guid path := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + account.Guid
if err = os.RemoveAll(path); err != nil { if err = os.RemoveAll(path); err != nil {
ErrMsg(err) ErrMsg(err)
} }

View File

@ -98,7 +98,7 @@ func RemoveNodeAccount(w http.ResponseWriter, r *http.Request) {
} }
// delete asset files // delete asset files
path := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + account.Guid path := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + account.Guid
if err = os.RemoveAll(path); err != nil { if err = os.RemoveAll(path); err != nil {
ErrMsg(err) ErrMsg(err)
} }

View File

@ -47,6 +47,7 @@ const APP_QUEUEAUDIO = "audio"
const APP_QUEUEVIDEO = "video" const APP_QUEUEVIDEO = "video"
const APP_QUEUEPHOTO = "photo" const APP_QUEUEPHOTO = "photo"
const APP_QUEUEDEFAULT = "" const APP_QUEUEDEFAULT = ""
const APP_DEFAULTPATH = "./asset"
func AppCardStatus(status string) bool { func AppCardStatus(status string) bool {
if status == APP_CARDPENDING { if status == APP_CARDPENDING {

View File

@ -60,7 +60,7 @@ func AccountLogin(r *http.Request) (*store.Account, error) {
return account, nil return account, nil
} }
func BearerAccountToken(r *http.Request) (store.AccountToken, error) { func BearerAccountToken(r *http.Request) (*store.AccountToken, error) {
// parse bearer authentication // parse bearer authentication
auth := r.Header.Get("Authorization") auth := r.Header.Get("Authorization")
@ -69,12 +69,12 @@ func BearerAccountToken(r *http.Request) (store.AccountToken, error) {
// find token record // find token record
var accountToken store.AccountToken var accountToken store.AccountToken
if err := store.DB.Preload("Account").Where("token = ?", token).First(&accountToken).Error; err != nil { if err := store.DB.Preload("Account").Where("token = ?", token).First(&accountToken).Error; err != nil {
return accountToken, err return nil, err
} }
if accountToken.Expires < time.Now().Unix() { if accountToken.Expires < time.Now().Unix() {
return accountToken, errors.New("expired token") return nil, errors.New("expired token")
} }
return accountToken, nil return &accountToken, nil
} }
func BearerAppToken(r *http.Request, detail bool) (*store.Account, int, error) { func BearerAppToken(r *http.Request, detail bool) (*store.Account, int, error) {

View File

@ -13,7 +13,7 @@ func garbageCollect(act *store.Account) {
defer garbageSync.Unlock() defer garbageSync.Unlock()
// get all asset files // get all asset files
dir := getStrConfigValue(CONFIG_ASSETPATH, ".") + "/" + act.Guid dir := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH) + "/" + act.Guid
files, err := os.ReadDir(dir) files, err := os.ReadDir(dir)
if err != nil { if err != nil {
ErrMsg(err) ErrMsg(err)

View File

@ -89,7 +89,7 @@ func transcodeDefault() {
func transcodeAsset(asset *store.Asset) { func transcodeAsset(asset *store.Asset) {
// prepare script path // prepare script path
data := getStrConfigValue(CONFIG_ASSETPATH, ".") data := getStrConfigValue(CONFIG_ASSETPATH, APP_DEFAULTPATH)
script := getStrConfigValue(CONFIG_SCRIPTPATH, ".") script := getStrConfigValue(CONFIG_SCRIPTPATH, ".")
re := regexp.MustCompile("^[a-zA-Z0-9_]*$") re := regexp.MustCompile("^[a-zA-Z0-9_]*$")

View File

@ -1,14 +1,54 @@
import React, { useState } from 'react' import React, { useState, useEffect } from 'react'
import login from './login.png'; import login from './login.png';
import { Input, Button } from 'antd'; import { Input, Button } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons'; import { UserOutlined, LockOutlined } from '@ant-design/icons';
import 'antd/dist/antd.css'; import 'antd/dist/antd.css';
const FETCH_TIMEOUT = 15000;
function checkResponse(response) {
if(response.status >= 400 && response.status < 600) {
throw new Error(response.url + " failed");
}
}
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')), FETCH_TIMEOUT))
]);
}
async function getAvailable() {
let available = await fetchWithTimeout("/account/available", { method: 'GET', timeout: FETCH_TIMEOUT } );
checkResponse(available);
return await available.json()
}
function App() { function App() {
const [available, setAvailable] = useState(0)
const [username, setUsername] = useState('') const [username, setUsername] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
useEffect(() => {
getAvailable().then(a => {
setAvailable(a)
console.log(a)
}).catch(err => {
console.log(err)
})
}, [])
const Create = () => {
if (available > 0) {
return <Button type="link" onClick={onCreate} style={{ marginTop: '4px', color: '#000044' }}>Create Account</Button>
}
return <></>
}
const onLogin = () => { const onLogin = () => {
console.log(username) console.log(username)
console.log(password) console.log(password)
@ -31,7 +71,7 @@ function App() {
<Input.Password size="large" onChange={(e) => setPassword(e.target.value)} placeholder="password" prefix={<LockOutlined />} style={{ marginTop: '16px' }} /> <Input.Password size="large" onChange={(e) => setPassword(e.target.value)} placeholder="password" prefix={<LockOutlined />} style={{ marginTop: '16px' }} />
<Button type="primary" onClick={onLogin} style={{ alignSelf: 'center', marginTop: '16px', width: '33%' }}>Sign In</Button> <Button type="primary" onClick={onLogin} style={{ alignSelf: 'center', marginTop: '16px', width: '33%' }}>Sign In</Button>
</div> </div>
<Button type="link" onClick={onCreate} style={{ marginTop: '4px', color: '#000044' }}>Create Account</Button> <Create />
</div> </div>
</div> </div>
); );