Demo mode: automatically connect as demo user

This commit is contained in:
2026-02-24 19:02:38 +01:00
parent c50c6901b9
commit 18b5f0f0e1
7 changed files with 50 additions and 20 deletions

View File

@@ -4,7 +4,7 @@
import NavBarSearch from './NavBarSearch.vue' import NavBarSearch from './NavBarSearch.vue'
import BarcodeModal from './BarcodeModal.vue' import BarcodeModal from './BarcodeModal.vue'
import { useAuthStore } from './auth.store.js' import { useAuthStore } from './auth.store.js'
import { getAppInfo } from './api.js' import { getAppInfo, postLogin } from './api.js'
import { onMounted } from 'vue' import { onMounted } from 'vue'
const authStore = useAuthStore(); const authStore = useAuthStore();
@@ -21,8 +21,23 @@
const appInfo = ref(null); const appInfo = ref(null);
const appInfoErr = ref(null); const appInfoErr = ref(null);
async function logInIfDemoMode(demoMode) {
if (!demoMode) {
return;
}
const demouser = ref({
username: "demo",
password: ""
});
const res = await postLogin(demouser)
const json = await res.json();
await useAuthStore().login({username: demouser.value.username, token: json["token"]})
}
onMounted(() => { onMounted(() => {
getAppInfo(appInfo, appInfoErr); getAppInfo(appInfo, appInfoErr)
.then(() => logInIfDemoMode(appInfo.value.demoMode));
}) })
</script> </script>
@@ -62,7 +77,7 @@
<RouterLink v-if="authStore.user" to="/add" class="navbar-item" activeClass="is-active"> <RouterLink v-if="authStore.user" to="/add" class="navbar-item" activeClass="is-active">
{{ $t('navbar.addbook')}} {{ $t('navbar.addbook')}}
</RouterLink> </RouterLink>
<div v-if="authStore.user" class="navbar-item is-hidden-desktop"> <div v-if="authStore.user && appInfo && !appInfo.demoMode" class="navbar-item is-hidden-desktop">
<a @click="logout"> <a @click="logout">
{{ $t('navbar.logout')}} {{ $t('navbar.logout')}}
<span class="icon" :title="$t('navbar.logout')"> <span class="icon" :title="$t('navbar.logout')">
@@ -76,7 +91,7 @@
<div > <div >
{{ authStore.user.username }} {{ authStore.user.username }}
</div> </div>
<a @click="logout" class="button is-light"> <a v-if="appInfo && !appInfo.demoMode" @click="logout" class="button is-light">
{{ $t('navbar.logout')}} {{ $t('navbar.logout')}}
</a> </a>
</div> </div>

View File

@@ -34,8 +34,8 @@ function useFetch(data, error, url) {
} }
} }
export function getAppInfo(appInfo, appInfoErr) { export async function getAppInfo(appInfo, appInfoErr) {
fetch('/ws/appinfo', { return fetch('/ws/appinfo', {
method: 'GET' method: 'GET'
}).then((res) => res.json()) }).then((res) => res.json())
.then((json) => appInfo.value = json) .then((json) => appInfo.value = json)

View File

@@ -27,4 +27,5 @@ func TestGetAppInfo_Ok(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, false, appInfo.RegistrationDisabled) assert.Equal(t, false, appInfo.RegistrationDisabled)
assert.Equal(t, false, appInfo.DemoMode)
} }

View File

@@ -19,6 +19,7 @@ type Config struct {
Limit int `toml:"limit" default:"100" comment:"A single API call will return at most this number of records."` Limit int `toml:"limit" default:"100" comment:"A single API call will return at most this number of records."`
InventaireUrl string `toml:"inventaire-url" default:"https://inventaire.io" comment:"An inventaire.io instance URL."` InventaireUrl string `toml:"inventaire-url" default:"https://inventaire.io" comment:"An inventaire.io instance URL."`
DisableRegistration bool `toml:"disable-registration" default:"false" comment:"Disable new account creation."` DisableRegistration bool `toml:"disable-registration" default:"false" comment:"Disable new account creation."`
DemoMode bool `toml:"demo-mode" default:"false" comment:"Activate demo mode: anyone connecting to the instance will be logged in as user 'demo'"`
} }
func defaultConfig() Config { func defaultConfig() Config {
@@ -31,6 +32,7 @@ func defaultConfig() Config {
Limit: 100, Limit: 100,
InventaireUrl: "https://inventaire.io", InventaireUrl: "https://inventaire.io",
DisableRegistration: false, DisableRegistration: false,
DemoMode: false,
} }
} }

View File

@@ -2,6 +2,7 @@ package dto
type AppInfo struct { type AppInfo struct {
RegistrationDisabled bool `json:"registrationDisabled"` RegistrationDisabled bool `json:"registrationDisabled"`
DemoMode bool `json:"demoMode"`
} }
type BookGet struct { type BookGet struct {

View File

@@ -8,5 +8,8 @@ import (
) )
func GetAppInfo(ac appcontext.AppContext) { func GetAppInfo(ac appcontext.AppContext) {
ac.C.JSON(http.StatusOK, dto.AppInfo{RegistrationDisabled: ac.Config.DisableRegistration}) ac.C.JSON(http.StatusOK, dto.AppInfo{
RegistrationDisabled: ac.Config.DisableRegistration,
DemoMode: ac.Config.DemoMode,
})
} }

View File

@@ -16,6 +16,10 @@ import (
) )
func PostLoginHandler(ac appcontext.AppContext) { func PostLoginHandler(ac appcontext.AppContext) {
var username string
if !ac.Config.DemoMode {
var user dto.UserLogin var user dto.UserLogin
err := ac.C.ShouldBindJSON(&user) err := ac.C.ShouldBindJSON(&user)
if err != nil { if err != nil {
@@ -23,14 +27,18 @@ func PostLoginHandler(ac appcontext.AppContext) {
return return
} }
if !isUserAndPasswordOk(ac.Db, user.Username, user.Password) { if !ac.Config.DemoMode && !isUserAndPasswordOk(ac.Db, user.Username, user.Password) {
ac.C.JSON(http.StatusUnauthorized, ac.C.JSON(http.StatusUnauthorized,
gin.H{"error": i18nresource.GetTranslatedMessage(&ac, "InvalidCredentials")}) gin.H{"error": i18nresource.GetTranslatedMessage(&ac, "InvalidCredentials")})
return return
} }
username = user.Username
} else {
username = "demo"
}
var jwtToken string var jwtToken string
jwtToken, err = jwtauth.GenerateJwtToken(user.Username) jwtToken, err := jwtauth.GenerateJwtToken(username)
if err != nil { if err != nil {
ac.C.JSON(http.StatusUnauthorized, ac.C.JSON(http.StatusUnauthorized,
gin.H{"error": fmt.Errorf("Error when generating JWT token: %w", err)}) gin.H{"error": fmt.Errorf("Error when generating JWT token: %w", err)})