P3a: org carries plan/seats/active/created_at; create_user enforces seats + rejects inactive org; verify blocks login for inactive orgs; PATCH /api/admin/orgs (super_admin) updates plan/seats/active with audit. Fixed verify swallowing its AuthError. P3b: export/token issues a 5-min HMAC one-time CSV link; export accepts ?token=. P3c: setup requires accepted_terms (consent stored); Setup.vue consent checkbox. All 8 backend suites pass. Rebuilt dist.
53 lines
1.3 KiB
JavaScript
53 lines
1.3 KiB
JavaScript
// Auth + role store (reactive).
|
|
import { reactive } from 'vue'
|
|
import { getToken, setToken, api } from '../api'
|
|
|
|
export const auth = reactive({
|
|
user: null,
|
|
token: getToken(),
|
|
mustSetup: false,
|
|
get role() {
|
|
return this.user ? this.user.role : null
|
|
},
|
|
get isAdmin() {
|
|
return this.role === 'admin' || this.role === 'super_admin'
|
|
},
|
|
get isSuperAdmin() {
|
|
return this.role === 'super_admin'
|
|
},
|
|
async load() {
|
|
if (!this.token) return null
|
|
try {
|
|
const data = await api.me()
|
|
this.user = data.user
|
|
this.mustSetup = !!data.user?.must_setup
|
|
return this.user
|
|
} catch (e) {
|
|
this.user = null
|
|
this.mustSetup = false
|
|
setToken(null)
|
|
return null
|
|
}
|
|
},
|
|
async login(username, password) {
|
|
const data = await api.login(username, password)
|
|
this.token = data.token
|
|
setToken(data.token)
|
|
this.user = data.user
|
|
this.mustSetup = !!data.must_setup
|
|
return data.user
|
|
},
|
|
async finishSetup(email, password, acceptedTerms = false) {
|
|
const data = await api.setup({ username: this.user.username || this.user.id, email, password, accepted_terms: acceptedTerms })
|
|
this.user = data.user
|
|
this.mustSetup = false
|
|
return data.user
|
|
},
|
|
logout() {
|
|
this.user = null
|
|
this.token = null
|
|
this.mustSetup = false
|
|
setToken(null)
|
|
},
|
|
})
|