mirror of
https://github.com/open-webui/open-webui
synced 2025-04-24 16:32:11 +00:00
Implement Emailing, Implement Add-User Flow
This commit is contained in:
parent
7ad498bfda
commit
ff68a9ae80
@ -9,6 +9,7 @@ from open_webui.internal.db import get_db, Base
|
|||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
NO_COMPANY = "NO_COMPANY"
|
NO_COMPANY = "NO_COMPANY"
|
||||||
|
EIGHTY_PERCENT_CREDIT_LIMIT = 4000
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Company DB Schema
|
# Company DB Schema
|
||||||
@ -179,9 +180,4 @@ class CompanyTable:
|
|||||||
company = db.query(Company).filter(Company.id == company_id).first()
|
company = db.query(Company).filter(Company.id == company_id).first()
|
||||||
return company.credit_balance if company else None
|
return company.credit_balance if company else None
|
||||||
|
|
||||||
def has_sufficient_credits(self, company_id: str, required_credits: int) -> bool:
|
|
||||||
"""Check if company has sufficient credits for an operation"""
|
|
||||||
balance = self.get_credit_balance(company_id)
|
|
||||||
return balance is None or balance >= required_credits # None means unlimited
|
|
||||||
|
|
||||||
Companies = CompanyTable()
|
Companies = CompanyTable()
|
@ -3,6 +3,8 @@ import uuid
|
|||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
|
|
||||||
from open_webui.models.auths import (
|
from open_webui.models.auths import (
|
||||||
@ -19,6 +21,7 @@ from open_webui.models.auths import (
|
|||||||
UserResponse,
|
UserResponse,
|
||||||
)
|
)
|
||||||
from beyond_the_loop.models.users import Users
|
from beyond_the_loop.models.users import Users
|
||||||
|
from beyond_the_loop.services.email_service import EmailService
|
||||||
from beyond_the_loop.models.companies import NO_COMPANY
|
from beyond_the_loop.models.companies import NO_COMPANY
|
||||||
|
|
||||||
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
||||||
@ -550,6 +553,19 @@ async def signout(request: Request, response: Response):
|
|||||||
############################
|
############################
|
||||||
|
|
||||||
|
|
||||||
|
def generate_secure_password(length=12):
|
||||||
|
"""Generate a secure random password."""
|
||||||
|
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||||
|
while True:
|
||||||
|
password = ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||||
|
# Check if password has at least one of each: uppercase, lowercase, digit, special char
|
||||||
|
if (any(c.isupper() for c in password)
|
||||||
|
and any(c.islower() for c in password)
|
||||||
|
and any(c.isdigit() for c in password)
|
||||||
|
and any(c in "!@#$%^&*" for c in password)):
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
@router.post("/add", response_model=SigninResponse)
|
@router.post("/add", response_model=SigninResponse)
|
||||||
async def add_user(form_data: AddUserForm, admin_user: Users = Depends(get_admin_user)):
|
async def add_user(form_data: AddUserForm, admin_user: Users = Depends(get_admin_user)):
|
||||||
if not validate_email_format(form_data.email.lower()):
|
if not validate_email_format(form_data.email.lower()):
|
||||||
@ -561,7 +577,10 @@ async def add_user(form_data: AddUserForm, admin_user: Users = Depends(get_admin
|
|||||||
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hashed = get_password_hash(form_data.password)
|
# Generate a secure random password
|
||||||
|
password = generate_secure_password()
|
||||||
|
hashed = get_password_hash(password)
|
||||||
|
|
||||||
new_user = Auths.insert_new_auth(
|
new_user = Auths.insert_new_auth(
|
||||||
form_data.email.lower(),
|
form_data.email.lower(),
|
||||||
hashed,
|
hashed,
|
||||||
@ -572,6 +591,14 @@ async def add_user(form_data: AddUserForm, admin_user: Users = Depends(get_admin
|
|||||||
)
|
)
|
||||||
|
|
||||||
if new_user:
|
if new_user:
|
||||||
|
# Send welcome email with the generated password
|
||||||
|
email_service = EmailService()
|
||||||
|
email_service.send_welcome_mail(
|
||||||
|
to_email=form_data.email.lower(),
|
||||||
|
username=form_data.name,
|
||||||
|
password=password
|
||||||
|
)
|
||||||
|
|
||||||
token = create_token(data={"id": new_user.id})
|
token = create_token(data={"id": new_user.id})
|
||||||
return {
|
return {
|
||||||
"token": token,
|
"token": token,
|
||||||
|
@ -19,7 +19,9 @@ from starlette.background import BackgroundTask
|
|||||||
from beyond_the_loop.models.models import Models
|
from beyond_the_loop.models.models import Models
|
||||||
from beyond_the_loop.models.model_message_credit_costs import ModelMessageCreditCosts
|
from beyond_the_loop.models.model_message_credit_costs import ModelMessageCreditCosts
|
||||||
from beyond_the_loop.models.companies import Companies
|
from beyond_the_loop.models.companies import Companies
|
||||||
|
from beyond_the_loop.models.companies import EIGHTY_PERCENT_CREDIT_LIMIT
|
||||||
from beyond_the_loop.models.completions import Completions
|
from beyond_the_loop.models.completions import Completions
|
||||||
|
from beyond_the_loop.services.email_service import EmailService
|
||||||
|
|
||||||
from open_webui.config import (
|
from open_webui.config import (
|
||||||
CACHE_DIR,
|
CACHE_DIR,
|
||||||
@ -565,13 +567,24 @@ async def generate_chat_completion(
|
|||||||
# Subtract credits from the company's balance (if possible)
|
# Subtract credits from the company's balance (if possible)
|
||||||
model_message_credit_cost = ModelMessageCreditCosts.get_cost_by_model(model_id)
|
model_message_credit_cost = ModelMessageCreditCosts.get_cost_by_model(model_id)
|
||||||
|
|
||||||
|
# Get current credit balance once
|
||||||
|
current_balance = Companies.get_credit_balance(user.company_id)
|
||||||
|
|
||||||
# Check if company has sufficient credits
|
# Check if company has sufficient credits
|
||||||
if not Companies.has_sufficient_credits(user.company_id, model_message_credit_cost):
|
if current_balance < model_message_credit_cost:
|
||||||
|
email_service = EmailService()
|
||||||
|
email_service.send_budget_mail_100(to_email=user.email, recipient_name=user.name)
|
||||||
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=402, # 402 Payment Required
|
status_code=402, # 402 Payment Required
|
||||||
detail=f"Insufficient credits. This operation requires {model_message_credit_cost} credits.",
|
detail=f"Insufficient credits. This operation requires {model_message_credit_cost} credits.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check 80% threshold
|
||||||
|
if current_balance - model_message_credit_cost < EIGHTY_PERCENT_CREDIT_LIMIT: # If balance is less than 125% of required (which means we're below 80%)
|
||||||
|
email_service = EmailService()
|
||||||
|
email_service.send_budget_mail_80(to_email=user.email, recipient_name=user.name)
|
||||||
|
|
||||||
# Subtract credits from balance
|
# Subtract credits from balance
|
||||||
Companies.subtract_credit_balance(user.company_id, model_message_credit_cost)
|
Companies.subtract_credit_balance(user.company_id, model_message_credit_cost)
|
||||||
|
|
||||||
|
117
backend/beyond_the_loop/services/email_service.py
Normal file
117
backend/beyond_the_loop/services/email_service.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
from typing import Dict, Optional, List
|
||||||
|
import sib_api_v3_sdk
|
||||||
|
from sib_api_v3_sdk.rest import ApiException
|
||||||
|
from pydantic import EmailStr
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
class EmailService:
|
||||||
|
def __init__(self):
|
||||||
|
self.configuration = sib_api_v3_sdk.Configuration()
|
||||||
|
self.configuration.api_key['api-key'] = os.getenv('BREVO_API_KEY')
|
||||||
|
self.api_instance = sib_api_v3_sdk.TransactionalEmailsApi(sib_api_v3_sdk.ApiClient(self.configuration))
|
||||||
|
|
||||||
|
# Initialize Jinja2 environment
|
||||||
|
template_dir = Path(__file__).parent.parent / 'templates' / 'email'
|
||||||
|
self.jinja_env = Environment(loader=FileSystemLoader(template_dir))
|
||||||
|
|
||||||
|
def send_welcome_mail(self, to_email: EmailStr, username: str, password: str) -> bool:
|
||||||
|
"""Send a welcome email to newly registered users."""
|
||||||
|
try:
|
||||||
|
subject = "Willkommen bei Bchat!"
|
||||||
|
sender = {"name": "Bchat", "email": os.getenv('SENDER_EMAIL', 'noreply@beyondtheloop.ai')}
|
||||||
|
to = [{"email": to_email, "name": username}]
|
||||||
|
|
||||||
|
# Load and render the template
|
||||||
|
template = self.jinja_env.get_template('welcome.html')
|
||||||
|
html_content = template.render(
|
||||||
|
name=username,
|
||||||
|
password=password
|
||||||
|
)
|
||||||
|
|
||||||
|
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(
|
||||||
|
to=to,
|
||||||
|
html_content=html_content,
|
||||||
|
sender=sender,
|
||||||
|
subject=subject
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_instance.send_transac_email(send_smtp_email)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except ApiException as e:
|
||||||
|
print(f"Exception when sending registration email: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def send_custom_email(self, to_email: EmailStr, subject: str, html_content: str,
|
||||||
|
recipient_name: Optional[str] = None) -> bool:
|
||||||
|
"""Send a custom email with specified content."""
|
||||||
|
try:
|
||||||
|
sender = {"name": "Beyond The Loop", "email": os.getenv('SENDER_EMAIL', 'noreply@beyondtheloop.com')}
|
||||||
|
to = [{"email": to_email, "name": recipient_name or to_email}]
|
||||||
|
|
||||||
|
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(
|
||||||
|
to=to,
|
||||||
|
html_content=html_content,
|
||||||
|
sender=sender,
|
||||||
|
subject=subject
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_instance.send_transac_email(send_smtp_email)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except ApiException as e:
|
||||||
|
print(f"Exception when sending custom email: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_budget_mail_80(self, to_email: EmailStr, recipient_name: Optional[str] = None) -> bool:
|
||||||
|
"""Send a warning email when budget reaches 80% of limit."""
|
||||||
|
try:
|
||||||
|
subject = "Abrechnungslimit fast erreicht"
|
||||||
|
sender = {"name": "Beyond The Loop", "email": os.getenv('SENDER_EMAIL', 'noreply@beyondtheloop.ai')}
|
||||||
|
to = [{"email": to_email, "name": recipient_name or to_email}]
|
||||||
|
|
||||||
|
# Load and render the template
|
||||||
|
template = self.jinja_env.get_template('budget-mail-80.html')
|
||||||
|
html_content = template.render()
|
||||||
|
|
||||||
|
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(
|
||||||
|
to=to,
|
||||||
|
html_content=html_content,
|
||||||
|
sender=sender,
|
||||||
|
subject=subject
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_instance.send_transac_email(send_smtp_email)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except ApiException as e:
|
||||||
|
print(f"Exception when sending budget warning (80%) email: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_budget_mail_100(self, to_email: EmailStr, recipient_name: Optional[str] = None) -> bool:
|
||||||
|
"""Send a critical warning email when budget reaches 100% of limit."""
|
||||||
|
try:
|
||||||
|
subject = "Achtung: Abrechnungslimit erreicht!"
|
||||||
|
sender = {"name": "Beyond The Loop", "email": os.getenv('SENDER_EMAIL', 'noreply@beyondtheloop.ai')}
|
||||||
|
to = [{"email": to_email, "name": recipient_name or to_email}]
|
||||||
|
|
||||||
|
# Load and render the template
|
||||||
|
template = self.jinja_env.get_template('budget-mail-100.html')
|
||||||
|
html_content = template.render()
|
||||||
|
|
||||||
|
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(
|
||||||
|
to=to,
|
||||||
|
html_content=html_content,
|
||||||
|
sender=sender,
|
||||||
|
subject=subject
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_instance.send_transac_email(send_smtp_email)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except ApiException as e:
|
||||||
|
print(f"Exception when sending budget warning (100%) email: {e}")
|
||||||
|
return False
|
78
backend/beyond_the_loop/templates/email/base.html
Normal file
78
backend/beyond_the_loop/templates/email/base.html
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% block title %}Bchat{% endblock %}</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #dddddd;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: block;
|
||||||
|
width: 200px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 10px 0;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #007FFF;
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #dddddd;
|
||||||
|
color: #999999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.footer a {
|
||||||
|
color: #007BFF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
{% block additional_styles %}{% endblock %}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>{% block header %}{% endblock %}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p>Diese E-Mail wurde automatisch generiert. Bitte antworte nicht direkt darauf. Bei Fragen wenden dich an unseren <a href="mailto:support@beyondtheloop.ai">Support</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
backend/beyond_the_loop/templates/email/budget-mail-100.html
Normal file
16
backend/beyond_the_loop/templates/email/budget-mail-100.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Achtung: Abrechnungslimit erreicht!{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}Achtung: Abrechnungslimit erreicht!{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p>Hi,</p>
|
||||||
|
<p>das festgelegte Abrechnungslimit für deinen Workspace wurde erreicht. Um Einschränkungen zu vermeiden, ist jetzt dringend deine Aktion erforderlich.</p>
|
||||||
|
<p>Ohne sofortige Erhöhung des Limits kann dein Team nicht mehr auf BChat zugreifen. Erhöhe das Limit einfach in deinen Unternehmenseinstellungen!</p>
|
||||||
|
<a href="https://bchat.example.com/billing" class="button">Jetzt Limit erhöhen</a>
|
||||||
|
<p>Falls der Button nicht funktioniert, kopiere bitte diese URL und füge sie in deinen Browser ein:</p>
|
||||||
|
<p><a href="https://bchat.example.com/billing">https://bchat.example.com/billing</a></p>
|
||||||
|
<p>Zögere nicht, uns bei Fragen zu kontaktieren.</p>
|
||||||
|
<p>Dein Beyond the Loop Team</p>
|
||||||
|
{% endblock %}
|
15
backend/beyond_the_loop/templates/email/budget-mail-80.html
Normal file
15
backend/beyond_the_loop/templates/email/budget-mail-80.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Abrechnungslimit fast erreicht{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}Dein Abrechnungslimit ist fast erreicht!{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p>Hallo,</p>
|
||||||
|
<p>dein Workspace hat gerade 80% des festgelegten Abrechnungslimits erreicht. Keine Sorge, alles läuft noch - aber es ist Zeit, einen Blick in deine Unternehmens-Dashboard zu werfen.</p>
|
||||||
|
<p>Schraube das Limit rechtzeitig hoch, um sicherzustellen, dass jeder die Platform ohne Einschränkungen nutzen kann.</p>
|
||||||
|
<a class="button" href="https://bchat.example.com/billing">Abrechnungsdetails überprüfen</a>
|
||||||
|
<p>Falls du Fragen hast oder Hilfe brauchst - wir sind für dich da!</p>
|
||||||
|
<p>Danke, dass du bChat nutzt!</p>
|
||||||
|
<p>Dein Beyond the Loop Team</p>
|
||||||
|
{% endblock %}
|
29
backend/beyond_the_loop/templates/email/welcome.html
Normal file
29
backend/beyond_the_loop/templates/email/welcome.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Account Aktivierung{% endblock %}
|
||||||
|
|
||||||
|
{% block additional_styles %}
|
||||||
|
.password {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #333333;
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}Willkommen bei Bchat!{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<p>Hallo {{ name }},</p>
|
||||||
|
<p>Du wurdest zu bChat eingeladen! Um loszulegen, logge dich mit deiner Email-Adresse und folgendem Passwort ein:</p>
|
||||||
|
<div class="password">{{ password }}</div>
|
||||||
|
<p>Viel Spaß beim Entdecken von bChat!</p>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
<p>Diese E-Mail wurde automatisch generiert. Bitte antworte nicht direkt darauf. Bei Fragen wenden dich an unseren <a href="mailto:support@beyondtheloop.ai">Support</a>.</p>
|
||||||
|
{% endblock %}
|
@ -90,8 +90,11 @@ class SignupForm(BaseModel):
|
|||||||
profile_image_url: Optional[str] = "/user.png"
|
profile_image_url: Optional[str] = "/user.png"
|
||||||
|
|
||||||
|
|
||||||
class AddUserForm(SignupForm):
|
class AddUserForm(BaseModel):
|
||||||
role: Optional[str] = "pending",
|
name: str
|
||||||
|
email: str
|
||||||
|
profile_image_url: Optional[str] = "/user.png"
|
||||||
|
role: Optional[str] = "pending"
|
||||||
|
|
||||||
|
|
||||||
class AuthsTable:
|
class AuthsTable:
|
||||||
|
@ -1 +1 @@
|
|||||||
Name,Email,Password,Role
|
Name,Email,Role
|
||||||
|
|
@ -106,3 +106,6 @@ google-cloud-storage==2.19.0
|
|||||||
|
|
||||||
## LDAP
|
## LDAP
|
||||||
ldap3==2.9.1
|
ldap3==2.9.1
|
||||||
|
|
||||||
|
jinja2==3.1.3
|
||||||
|
sib-api-v3-sdk==7.6.0
|
||||||
|
@ -353,7 +353,6 @@ export const addUser = async (
|
|||||||
token: string,
|
token: string,
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
password: string,
|
|
||||||
role: string = 'pending'
|
role: string = 'pending'
|
||||||
) => {
|
) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
@ -367,7 +366,6 @@ export const addUser = async (
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
|
||||||
role: role
|
role: role
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
let _user = {
|
let _user = {
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
|
||||||
role: 'user'
|
role: 'user'
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,7 +27,6 @@
|
|||||||
_user = {
|
_user = {
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
|
||||||
role: 'user'
|
role: 'user'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -42,15 +40,11 @@
|
|||||||
if (tab === '') {
|
if (tab === '') {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
const res = await addUser(
|
const res = await addUser(localStorage.token, _user.name, _user.email, _user.role).catch(
|
||||||
localStorage.token,
|
(error) => {
|
||||||
_user.name,
|
|
||||||
_user.email,
|
|
||||||
_user.password,
|
|
||||||
_user.role
|
|
||||||
).catch((error) => {
|
|
||||||
toast.error(`${error}`);
|
toast.error(`${error}`);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
stopLoading();
|
stopLoading();
|
||||||
@ -75,15 +69,14 @@
|
|||||||
|
|
||||||
if (idx > 0) {
|
if (idx > 0) {
|
||||||
if (
|
if (
|
||||||
columns.length === 4 &&
|
columns.length === 3 &&
|
||||||
['admin', 'user', 'pending'].includes(columns[3].toLowerCase())
|
['admin', 'user', 'pending'].includes(columns[2].toLowerCase())
|
||||||
) {
|
) {
|
||||||
const res = await addUser(
|
const res = await addUser(
|
||||||
localStorage.token,
|
localStorage.token,
|
||||||
columns[0],
|
columns[0], // name
|
||||||
columns[1],
|
columns[1], // email
|
||||||
columns[2],
|
columns[2].toLowerCase() // role
|
||||||
columns[3].toLowerCase()
|
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
toast.error(`Row ${idx + 1}: ${error}`);
|
toast.error(`Row ${idx + 1}: ${error}`);
|
||||||
return null;
|
return null;
|
||||||
@ -201,7 +194,7 @@
|
|||||||
class="w-full text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
|
class="w-full text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={_user.name}
|
bind:value={_user.name}
|
||||||
placeholder={$i18n.t('Enter Your Full Name')}
|
placeholder={$i18n.t('Enter Full Name')}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@ -218,25 +211,11 @@
|
|||||||
class="w-full text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
|
class="w-full text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
|
||||||
type="email"
|
type="email"
|
||||||
bind:value={_user.email}
|
bind:value={_user.email}
|
||||||
placeholder={$i18n.t('Enter Your Email')}
|
placeholder={$i18n.t('Enter Email')}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col w-full mt-1">
|
|
||||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Password')}</div>
|
|
||||||
|
|
||||||
<div class="flex-1">
|
|
||||||
<input
|
|
||||||
class="w-full text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
|
|
||||||
type="password"
|
|
||||||
bind:value={_user.password}
|
|
||||||
placeholder={$i18n.t('Enter Your Password')}
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else if tab === 'import'}
|
{:else if tab === 'import'}
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-3 w-full">
|
<div class="mb-3 w-full">
|
||||||
@ -265,7 +244,7 @@
|
|||||||
|
|
||||||
<div class=" text-xs text-gray-500">
|
<div class=" text-xs text-gray-500">
|
||||||
ⓘ {$i18n.t(
|
ⓘ {$i18n.t(
|
||||||
'Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.'
|
'Ensure your CSV file includes 4 columns in this order: Name, Email, Role.'
|
||||||
)}
|
)}
|
||||||
<a
|
<a
|
||||||
class="underline dark:text-gray-200"
|
class="underline dark:text-gray-200"
|
||||||
|
Loading…
Reference in New Issue
Block a user