feat: enhance admin setup with email validation and update workflows for fresh lib cloning

pull/10/head
David Montero Crespo 2026-03-07 00:00:22 -03:00
parent 290b149855
commit 41d8e25843
5 changed files with 26 additions and 14 deletions

View File

@ -13,14 +13,21 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
# Do NOT use submodules: recursive — the submodule pointers in this repo
# are stale and predate package.json. We clone the libs fresh below.
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '22'
# Clone wokwi-libs fresh (stale submodule pointers can't be used)
- name: Clone wokwi-libs
run: |
git clone --depth=1 https://github.com/wokwi/avr8js.git wokwi-libs/avr8js
git clone --depth=1 https://github.com/wokwi/rp2040js.git wokwi-libs/rp2040js
git clone --depth=1 https://github.com/wokwi/wokwi-elements.git wokwi-libs/wokwi-elements
# Cache wokwi-libs node_modules to speed up repeated runs
- name: Cache wokwi-libs node_modules
uses: actions/cache@v4

View File

@ -46,20 +46,14 @@ async def setup_admin(body: AdminSetupRequest, db: AsyncSession = Depends(get_db
# Check uniqueness
conflict = await db.execute(
select(User).where(User.username == username)
select(User).where((User.username == username) | (User.email == body.email))
)
if conflict.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Username already taken.")
# Generate a placeholder email for the admin setup account
email = f"{username}@admin.local"
email_conflict = await db.execute(select(User).where(User.email == email))
if email_conflict.scalar_one_or_none():
email = f"{username}.admin@admin.local"
raise HTTPException(status_code=400, detail="Username or email already taken.")
user = User(
username=username,
email=email,
email=body.email,
hashed_password=hash_password(body.password),
is_admin=True,
is_active=True,

View File

@ -5,6 +5,7 @@ from pydantic import BaseModel, EmailStr
class AdminSetupRequest(BaseModel):
username: str
email: EmailStr
password: str

View File

@ -142,6 +142,7 @@ const modalStyles: Record<string, React.CSSProperties> = {
function SetupScreen({ onDone }: { onDone: () => void }) {
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirm, setConfirm] = useState('');
const [error, setError] = useState('');
@ -157,7 +158,7 @@ function SetupScreen({ onDone }: { onDone: () => void }) {
}
setLoading(true);
try {
await createFirstAdmin(username, password);
await createFirstAdmin(username, email, password);
onDone();
navigate('/login?redirect=/admin');
} catch (err: any) {
@ -183,6 +184,15 @@ function SetupScreen({ onDone }: { onDone: () => void }) {
autoFocus
placeholder="admin"
/>
<label style={s.label}>Email</label>
<input
style={s.input}
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
placeholder="admin@example.com"
/>
<label style={s.label}>Password</label>
<input
style={s.input}

View File

@ -41,8 +41,8 @@ export async function getAdminSetupStatus(): Promise<{ has_admin: boolean }> {
return data;
}
export async function createFirstAdmin(username: string, password: string): Promise<AdminUserResponse> {
const { data } = await api.post('/admin/setup', { username, password });
export async function createFirstAdmin(username: string, email: string, password: string): Promise<AdminUserResponse> {
const { data } = await api.post('/admin/setup', { username, email, password });
return data;
}