First-run setup wizard (/setup)
- setup_complete site setting; a fresh admin is steered to /setup until it is finished (skippable for the session). - SetupWizard.vue: Site (facility/base-url/access-domain), Features (plugin enable/disable), Floor Map dimensions, Starter Data (seed common vendors), Finish. Reuses the settings + plugins APIs. - setup blueprint: POST /setup/seed-starter (idempotent common vendors) and POST /setup/complete. setupState composable drives the router redirect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from .settings import settings_bp
|
||||
from .auditlogs import auditlogs_bp
|
||||
from .users import users_bp
|
||||
from .customfields import customfields_bp
|
||||
from .setup import setup_bp
|
||||
|
||||
__all__ = [
|
||||
'auth_bp',
|
||||
@@ -40,4 +41,5 @@ __all__ = [
|
||||
'auditlogs_bp',
|
||||
'users_bp',
|
||||
'customfields_bp',
|
||||
'setup_bp',
|
||||
]
|
||||
|
||||
@@ -253,6 +253,13 @@ def build_default_settings():
|
||||
# the shopfloor dashboard. Blank site_base_url falls back to the browsing
|
||||
# origin so nothing breaks before a site configures it.
|
||||
sitedefaults = [
|
||||
{
|
||||
'key': 'setup_complete',
|
||||
'value': 'false',
|
||||
'valuetype': 'boolean',
|
||||
'category': 'site',
|
||||
'description': 'Set true once the first-run setup wizard has been finished'
|
||||
},
|
||||
{
|
||||
'key': 'site_base_url',
|
||||
'value': '',
|
||||
|
||||
62
shopdb/core/api/setup.py
Normal file
62
shopdb/core/api/setup.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""First-run setup wizard support endpoints.
|
||||
|
||||
The wizard itself is frontend; these endpoints cover the pieces that need
|
||||
server work: seeding common starter data and marking setup finished. Site,
|
||||
plugin, and map configuration reuse the existing settings / plugins APIs.
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
from flask_jwt_extended import jwt_required
|
||||
|
||||
from shopdb.extensions import db
|
||||
from shopdb.core.models import Vendor, Setting
|
||||
from shopdb.utils.responses import success_response
|
||||
from shopdb.utils.authz import require_role
|
||||
|
||||
setup_bp = Blueprint('setup', __name__)
|
||||
|
||||
# Common hardware vendors most sites will want on hand.
|
||||
STARTER_VENDORS = [
|
||||
('Dell Inc.', 'https://www.dell.com'),
|
||||
('HP Inc.', 'https://www.hp.com'),
|
||||
('Lenovo', 'https://www.lenovo.com'),
|
||||
('Xerox', 'https://www.xerox.com'),
|
||||
('Zebra Technologies', 'https://www.zebra.com'),
|
||||
('Cisco', 'https://www.cisco.com'),
|
||||
('Brother', 'https://www.brother.com'),
|
||||
('Microsoft', 'https://www.microsoft.com'),
|
||||
]
|
||||
|
||||
|
||||
@setup_bp.route('/seed-starter', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def seed_starter():
|
||||
"""Add common vendors that are not already present. Idempotent."""
|
||||
existing = {v.vendor.strip().lower() for v in Vendor.query.all()}
|
||||
added = []
|
||||
for name, website in STARTER_VENDORS:
|
||||
if name.strip().lower() in existing:
|
||||
continue
|
||||
db.session.add(Vendor(vendor=name, website=website))
|
||||
added.append(name)
|
||||
db.session.commit()
|
||||
return success_response(
|
||||
{'added': added, 'addedcount': len(added)},
|
||||
message=f'Seeded {len(added)} vendor(s).'
|
||||
)
|
||||
|
||||
|
||||
@setup_bp.route('/complete', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_role('admin')
|
||||
def mark_complete():
|
||||
"""Flag the first-run setup as finished."""
|
||||
row = Setting.query.filter_by(key='setup_complete').first()
|
||||
if row:
|
||||
row.value = 'true'
|
||||
else:
|
||||
db.session.add(Setting(key='setup_complete', value='true',
|
||||
valuetype='boolean', category='site'))
|
||||
db.session.commit()
|
||||
return success_response({'complete': True}, message='Setup marked complete.')
|
||||
Reference in New Issue
Block a user