feat(templates): enforce uniqueness and improve safety

- Raise TemplateExistsError when duplicate detected
- Migrate placeholder syntax from {} to {{}}
- Add validation for:
  * Reserved keyword collisions
  * Injection attempt patterns
- Update all test cases
This commit is contained in:
YuehuCao 2025-08-18 22:15:43 +08:00
parent e2a049a658
commit 2213fa59b5
6 changed files with 255 additions and 274 deletions

View File

@ -44,10 +44,9 @@ class TemplateMessageManager:
)
result = await self.template_message_service.create_global_template(template)
action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created"
await self.module_logger.log_info(
info=f"Global template {action}",
properties={"template_id": template_id, "region": region, "action": action}
info="Global template created",
properties={"template_id": template_id, "region": region}
)
return result
@ -156,15 +155,12 @@ class TemplateMessageManager:
)
result = await self.template_message_service.create_tenant_template(tenant_id, template)
action = "skipped" if hasattr(result, "_is_existing") and result._is_existing else "created"
await self.module_logger.log_info(
info=f"Tenant template {action}",
info="Tenant template created",
properties={
"tenant_id": tenant_id,
"template_id": template_id,
"region": region,
"action": action
"region": region
}
)

View File

@ -9,6 +9,15 @@ from common.constants.region import UserRegion
class TemplateMessageHandler:
def __init__(self):
self.module_logger = ModuleLogger(sender_id="TemplateMessageHandler")
@staticmethod
def _normalize_placeholders(text: str) -> str:
"""Convert Handlebars-like placeholders {{name}} into Python str.format style {name}.
Does not touch CSS double braces like `.class {{ ... }}` because those won't match \w+.
"""
if not isinstance(text, str):
return text
return re.sub(r"\{\{(\w+)\}\}", r"{\1}", text)
async def verify_tenant_access(self, template_id: str, tenant_id: str, region: int) -> Optional[MessageTemplateDoc]:
"""get template by tenant and template ids with region"""
try:
@ -52,21 +61,16 @@ class TemplateMessageHandler:
})
if existing_template:
# if template already exists, skip creation
await self.module_logger.log_info(
info="Global template already exists, skipping creation",
info="Global template already exists",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as existing template
existing_template._is_existing = True
return existing_template
raise ValueError("Global template already exists")
else:
# if template does not exist, create new template
await template.create()
await self.module_logger.log_info(
info="Template created in database",
@ -76,8 +80,6 @@ class TemplateMessageHandler:
"region": template.region
}
)
# mark as new template
template._is_existing = False
return template
except Exception as e:
@ -294,21 +296,16 @@ class TemplateMessageHandler:
})
if existing_template:
# if template already exists, skip creation
await self.module_logger.log_info(
info="Tenant template already exists, skipping creation",
info="Tenant Template already exists",
properties={
"template_id": template.template_id,
"tenant_id": template.tenant_id,
"region": template.region
}
)
# mark as existing template
existing_template._is_existing = True
return existing_template
raise ValueError("Template already exists")
else:
# if template does not exist, create new template
await template.create()
await self.module_logger.log_info(
info="Template created in database",
@ -318,8 +315,6 @@ class TemplateMessageHandler:
"region": template.region
}
)
# mark as new template
template._is_existing = False
return template
except Exception as e:
@ -412,8 +407,12 @@ class TemplateMessageHandler:
async def validate_template_parameters(self, template: MessageTemplateDoc, properties: dict) -> list:
"""validate template parameters"""
try:
subject_placeholders = re.findall(r'\{(\w+)\}', template.subject)
body_placeholders = re.findall(r'\{(\w+)\}', template.body)
# Normalize double-curly placeholders to single-curly for extraction
normalized_subject = self._normalize_placeholders(template.subject)
normalized_body = self._normalize_placeholders(template.body)
subject_placeholders = re.findall(r'\{(\w+)\}', normalized_subject)
body_placeholders = re.findall(r'\{(\w+)\}', normalized_body)
all_placeholders = list(set(subject_placeholders + body_placeholders))
missing_params = set(all_placeholders) - set(properties.keys())
@ -461,10 +460,23 @@ class TemplateMessageHandler:
async def render_template(self, template: MessageTemplateDoc, properties: dict) -> dict:
"""render template"""
try:
required_params = await self.validate_template_parameters(template, properties)
# Build normalized copies for rendering
normalized_subject = self._normalize_placeholders(template.subject)
normalized_body = self._normalize_placeholders(template.body)
subject = template.subject.format(**properties)
body = template.body.format(**properties)
# Validate using normalized content
temp_for_validation = MessageTemplateDoc(
template_id=template.template_id,
tenant_id=template.tenant_id,
region=template.region,
subject=normalized_subject,
body=normalized_body,
is_active=template.is_active,
)
required_params = await self.validate_template_parameters(temp_for_validation, properties)
subject = normalized_subject.format(**properties)
body = normalized_body.format(**properties)
await self.module_logger.log_info(
info="Template rendered",

View File

@ -12,7 +12,6 @@ class MessageTemplateDoc(Document):
subject: str
body: str
is_active: bool = True
is_existing: bool = False
created_at: datetime = datetime.utcnow()
updated_at: Optional[datetime] = None
@ -52,7 +51,7 @@ class EmailSendStatusDoc(Document):
updated_at: Optional[datetime] = None
class Settings:
name = "email_sender_status_doc"
name = "email_send_status_doc"
indexes = [
"email_id",
"tenant_id"

View File

@ -183,7 +183,7 @@ class TemplateMessageService:
continue
# copy template to tenant, use unique template_id
tenant_template_id = f"{global_template.template_id}_tenant_{tenant_id}"
tenant_template_id = f"{tenant_id}_{global_template.template_id}"
new_template = MessageTemplateDoc(
template_id=tenant_template_id,
tenant_id=tenant_id,

View File

@ -33,7 +33,6 @@ class GlobalTemplateCreator:
# generate admin token
admin_payload = {
"id": "test_admin_user",
"role": 8, # ADMINISTRATOR = 8
"tenant_id": None,
"exp": datetime.now(timezone.utc) + timedelta(hours=1)
@ -42,9 +41,8 @@ class GlobalTemplateCreator:
# generate tenant token
tenant_payload = {
"id": "test_tenant_user",
"role": 2, # BUSINESS = 2
"tenant_id": "test_tenant_user",
"tenant_id": "magicleaps",
"exp": datetime.now(timezone.utc) + timedelta(hours=1)
}
tenant_token = jwt.encode(tenant_payload, secret_key, algorithm=algorithm)
@ -109,57 +107,57 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "assessment_result_notification",
"region": 1,
"subject": "笔试结果 - {candidate_name}",
"subject": "笔试结果 - {{candidate_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">尊敬的 {candidate_name}</h2>
<h2 style="color: #333;">尊敬的 {{candidate_name}}</h2>
<p>感谢您参加我们的笔试考核以下是您的评估结果</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📊 评估详情</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>候选人姓名</strong>{candidate_name}</li>
<li><strong>评估日期</strong>{assessment_date}</li>
<li><strong>评估时长</strong>{duration_minutes} 分钟</li>
<li><strong>候选人姓名</strong>{{candidate_name}}</li>
<li><strong>评估日期</strong>{{assessment_date}}</li>
<li><strong>评估时长</strong>{{duration_minutes}} 分钟</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🔍 执行结果</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>执行状态</strong>{execution_status}</li>
<li><strong>执行时间</strong>{execution_time} 毫秒</li>
<li><strong>测试用例通过率</strong>{test_cases_passed}/{total_test_cases} ({pass_rate}%)</li>
<li><strong>执行状态</strong>{{execution_status}}</li>
<li><strong>执行时间</strong>{{execution_time}} 毫秒</li>
<li><strong>测试用例通过率</strong>{{test_cases_passed}}/{{total_test_cases}} ({{pass_rate}}%)</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> 执行错误信息</h3>
<p>{execution_errors}</p>
<p>{{execution_errors}}</p>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📈 详细结果</h3>
<p>{detailed_results}</p>
<p>{{detailed_results}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">🎯 评估结论</h3>
<p>{assessment_conclusion}</p>
<p>{{assessment_conclusion}}</p>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>如有任何疑问请联系我们的技术团队</p>
<p><strong>邮箱</strong>{tech_support_email}</p>
<p><strong>电话</strong>{tech_support_phone}</p>
<p><strong>邮箱</strong>{{tech_support_email}}</p>
<p><strong>电话</strong>{{tech_support_phone}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>祝好<br>{company_name} 技术团队</p>
<p>祝好<br>{{company_name}} 技术团队</p>
</div>
</div>"""
}
@ -168,57 +166,57 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "assessment_result_notification",
"region": 0,
"subject": "Assessment Result - {candidate_name}",
"subject": "Assessment Result - {{candidate_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {candidate_name},</h2>
<h2 style="color: #333;">Dear {{candidate_name}},</h2>
<p>Thank you for participating in our assessment. Here are your evaluation results:</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📊 Assessment Details</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Candidate Name: </strong>{candidate_name}</li>
<li><strong>Assessment Date: </strong>{assessment_date}</li>
<li><strong>Duration: </strong>{duration_minutes} minutes</li>
<li><strong>Candidate Name: </strong>{{candidate_name}}</li>
<li><strong>Assessment Date: </strong>{{assessment_date}}</li>
<li><strong>Duration: </strong>{{duration_minutes}} minutes</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🔍 Execution Results</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Execution Status: </strong>{execution_status}</li>
<li><strong>Execution Time: </strong>{execution_time} milliseconds</li>
<li><strong>Test Cases Passed: </strong>{test_cases_passed}/{total_test_cases} ({pass_rate}%)</li>
<li><strong>Execution Status: </strong>{{execution_status}}</li>
<li><strong>Execution Time: </strong>{{execution_time}} milliseconds</li>
<li><strong>Test Cases Passed: </strong>{{test_cases_passed}}/{{total_test_cases}} ({{pass_rate}}%)</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> Execution Errors</h3>
<p>{execution_errors}</p>
<p>{{execution_errors}}</p>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📈 Detailed Results</h3>
<p>{detailed_results}</p>
<p>{{detailed_results}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">🎯 Assessment Conclusion</h3>
<p>{assessment_conclusion}</p>
<p>{{assessment_conclusion}}</p>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>If you have any questions, please contact our technical team:</p>
<p><strong>Email: </strong>{tech_support_email}</p>
<p><strong>Phone: </strong>{tech_support_phone}</p>
<p><strong>Email: </strong>{{tech_support_email}}</p>
<p><strong>Phone: </strong>{{tech_support_phone}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Best regards,<br>{company_name} Technical Team</p>
<p>Best regards,<br>{{company_name}} Technical Team</p>
</div>
</div>"""
}
@ -236,13 +234,13 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "deadline_reminder",
"region": 1,
"subject": "截止期限提醒 - {task_name}",
"subject": "截止期限提醒 - {{task_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">尊敬的 {recipient_name}</h2>
<h2 style="color: #333;">尊敬的 {{recipient_name}}</h2>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #ffc107;">
<h3 style="color: #856404; margin-top: 0;"> 截止期限提醒</h3>
@ -252,42 +250,42 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📋 任务详情</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>任务名称</strong>{task_name}</li>
<li><strong>任务描述</strong>{task_description}</li>
<li><strong>截止日期</strong>{deadline_date}</li>
<li><strong>剩余时间</strong>{remaining_time}</li>
<li><strong>任务名称</strong>{{task_name}}</li>
<li><strong>任务描述</strong>{{task_description}}</li>
<li><strong>截止日期</strong>{{deadline_date}}</li>
<li><strong>剩余时间</strong>{{remaining_time}}</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;"> 时间信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>当前时间</strong>{current_time}</li>
<li><strong>截止时间</strong>{deadline_time}</li>
<li><strong>剩余天数</strong>{days_remaining} </li>
<li><strong>当前时间</strong>{{current_time}}</li>
<li><strong>截止时间</strong>{{deadline_time}}</li>
<li><strong>剩余天数</strong>{{days_remaining}} </li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📝 任务要求</h3>
<p>{task_requirements}</p>
<p>{{task_requirements}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">🔗 相关链接</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>任务详情</strong><a href="{task_url}" style="color: #007bff;">{task_url}</a></li>
<li><strong>提交入口</strong><a href="{submission_url}" style="color: #007bff;">{submission_url}</a></li>
<li><strong>任务详情</strong><a href="{{task_url}}" style="color: #007bff;">{{task_url}}</a></li>
<li><strong>提交入口</strong><a href="{{submission_url}}" style="color: #007bff;">{{submission_url}}</a></li>
</ul>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>请务必在截止日期前完成相关任务如有任何问题请联系</p>
<p><strong>邮箱</strong>{contact_email}</p>
<p><strong>邮箱</strong>{{contact_email}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>谢谢<br>{company_name} 团队</p>
<p>谢谢<br>{{company_name}} 团队</p>
</div>
</div>"""
}
@ -296,13 +294,13 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "deadline_reminder",
"region": 0,
"subject": "Deadline Reminder - {task_name}",
"subject": "Deadline Reminder - {{task_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {recipient_name},</h2>
<h2 style="color: #333;">Dear {{recipient_name}},</h2>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #ffc107;">
<h3 style="color: #856404; margin-top: 0;"> Deadline Reminder</h3>
@ -312,42 +310,42 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📋 Task Details</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Task Name: </strong>{task_name}</li>
<li><strong>Task Description: </strong>{task_description}</li>
<li><strong>Deadline Date: </strong>{deadline_date}</li>
<li><strong>Time Remaining: </strong>{remaining_time}</li>
<li><strong>Task Name: </strong>{{task_name}}</li>
<li><strong>Task Description: </strong>{{task_description}}</li>
<li><strong>Deadline Date: </strong>{{deadline_date}}</li>
<li><strong>Time
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;"> Time Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Current Time: </strong>{current_time}</li>
<li><strong>Deadline Time: </strong>{deadline_time}</li>
<li><strong>Days Remaining: </strong>{days_remaining} days</li>
<li><strong>Current Time: </strong>{{current_time}}</li>
<li><strong>Deadline Time: </strong>{{deadline_time}}</li>
<li><strong>Days Remaining: </strong>{{days_remaining}} days</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📝 Task Requirements</h3>
<p>{task_requirements}</p>
<p>{{task_requirements}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">🔗 Related Links</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Task Details: </strong><a href="{task_url}" style="color: #007bff;">{task_url}</a></li>
<li><strong>Submission Portal: </strong><a href="{submission_url}" style="color: #007bff;">{submission_url}</a></li>
<li><strong>Task Details: </strong><a href="{{task_url}}" style="color: #007bff;">{{task_url}}</a></li>
<li><strong>Submission Portal: </strong><a href="{{submission_url}}" style="color: #007bff;">{{submission_url}}</a></li>
</ul>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>Please ensure to complete the related task before the deadline. If you have any questions, please contact:</p>
<p><strong>Email: </strong>{contact_email}</p>
<p><strong>Email: </strong>{{contact_email}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Thank you!<br>{company_name} Team</p>
<p>Thank you!<br>{{company_name}} Team</p>
</div>
</div>"""
}
@ -365,59 +363,59 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "interview_status_update",
"region": 1,
"subject": "面试状态更新 - {candidate_name}",
"subject": "面试状态更新 - {{candidate_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">尊敬的 {candidate_name}</h2>
<h2 style="color: #333;">尊敬的 {{candidate_name}}</h2>
<p>您的面试状态已更新</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 候选人信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>姓名</strong>{candidate_name}</li>
<li><strong>应聘职位</strong>{position_name}</li>
<li><strong>申请编号</strong>{application_id}</li>
<li><strong>姓名</strong>{{candidate_name}}</li>
<li><strong>应聘职位</strong>{{position_name}}</li>
<li><strong>申请编号</strong>{{application_id}}</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📊 状态更新</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>当前状态</strong>{current_status}</li>
<li><strong>更新日期</strong>{update_date}</li>
<li><strong>更新时间</strong>{update_time}</li>
<li><strong>当前状态</strong>{{current_status}}</li>
<li><strong>更新日期</strong>{{update_date}}</li>
<li><strong>更新时间</strong>{{update_time}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📋 状态详情</h3>
<p>{status_details}</p>
<p>{{status_details}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">📅 下一步安排</h3>
<p>{next_steps}</p>
<p>{{next_steps}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> 重要时间</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>面试时间</strong>{interview_time}</li>
<li><strong>面试地点</strong>{interview_location}</li>
<li><strong>面试官</strong>{interviewer_name}</li>
<li><strong>面试时间</strong>{{interview_time}}</li>
<li><strong>面试地点</strong>{{interview_location}}</li>
<li><strong>面试官</strong>{{interviewer_name}}</li>
</ul>
</div>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📞 联系方式</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>面试官邮箱</strong>{interviewer_email}</li>
<li><strong>面试官电话</strong>{interviewer_phone}</li>
<li><strong>公司地址</strong>{company_address}</li>
<li><strong>面试官邮箱</strong>{{interviewer_email}}</li>
<li><strong>面试官电话</strong>{{interviewer_phone}}</li>
<li><strong>公司地址</strong>{{company_address}}</li>
</ul>
</div>
@ -426,7 +424,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>祝面试顺利<br>{company_name} 人力资源部</p>
<p>祝面试顺利<br>{{company_name}} 人力资源部</p>
</div>
</div>"""
}
@ -435,59 +433,59 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "interview_status_update",
"region": 0,
"subject": "Interview Status Update - {candidate_name}",
"subject": "Interview Status Update - {{candidate_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {candidate_name},</h2>
<h2 style="color: #333;">Dear {{candidate_name}},</h2>
<p>Your interview status has been updated:</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 Candidate Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Name: </strong>{candidate_name}</li>
<li><strong>Position Applied: </strong>{position_name}</li>
<li><strong>Application ID: </strong>{application_id}</li>
<li><strong>Name: </strong>{{candidate_name}}</li>
<li><strong>Position Applied: </strong>{{position_name}}</li>
<li><strong>Application ID: </strong>{{application_id}}</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📊 Status Update</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Current Status: </strong>{current_status}</li>
<li><strong>Update Date: </strong>{update_date}</li>
<li><strong>Update Time: </strong>{update_time}</li>
<li><strong>Current Status: </strong>{{current_status}}</li>
<li><strong>Update Date: </strong>{{update_date}}</li>
<li><strong>Update Time: </strong>{{update_time}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📋 Status Details</h3>
<p>{status_details}</p>
<p>{{status_details}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">📅 Next Steps</h3>
<p>{next_steps}</p>
<p>{{next_steps}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> Important Times</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Interview Time: </strong>{interview_time}</li>
<li><strong>Interview Location: </strong>{interview_location}</li>
<li><strong>Interviewer: </strong>{interviewer_name}</li>
<li><strong>Interview Time: </strong>{{interview_time}}</li>
<li><strong>Interview Location: </strong>{{interview_location}}</li>
<li><strong>Interviewer: </strong>{{interviewer_name}}</li>
</ul>
</div>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📞 Contact Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Interviewer Email: </strong>{interviewer_email}</li>
<li><strong>Interviewer Phone: </strong>{interviewer_phone}</li>
<li><strong>Company Address: </strong>{company_address}</li>
<li><strong>Interviewer Email: </strong>{{interviewer_email}}</li>
<li><strong>Interviewer Phone: </strong>{{interviewer_phone}}</li>
<li><strong>Company Address: </strong>{{company_address}}</li>
</ul>
</div>
@ -496,7 +494,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Good luck with your interview!<br>{company_name} Human Resources Department</p>
<p>Good luck with your interview!<br>{{company_name}} Human Resources Department</p>
</div>
</div>"""
}
@ -514,16 +512,16 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "welcome_email",
"region": 1,
"subject": "欢迎加入 {company_name} - {new_employee_name}",
"subject": "欢迎加入 {{company_name}} - {{new_employee_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">亲爱的 {new_employee_name}</h2>
<h2 style="color: #333;">亲爱的 {{new_employee_name}}</h2>
<div style="background-color: #d4edda; padding: 20px; border-radius: 5px; margin: 20px 0; text-align: center;">
<h1 style="color: #155724; margin: 0;">🎉 欢迎加入 {company_name}</h1>
<h1 style="color: #155724; margin: 0;">🎉 欢迎加入 {{company_name}}</h1>
</div>
<p>我们很高兴地通知您您已成功加入我们的团队以下是您的入职信息</p>
@ -531,58 +529,58 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 员工信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>姓名</strong>{new_employee_name}</li>
<li><strong>员工编号</strong>{employee_id}</li>
<li><strong>部门</strong>{department}</li>
<li><strong>职位</strong>{position}</li>
<li><strong>入职日期</strong>{start_date}</li>
<li><strong>姓名</strong>{{new_employee_name}}</li>
<li><strong>员工编号</strong>{{employee_id}}</li>
<li><strong>部门</strong>{{department}}</li>
<li><strong>职位</strong>{{position}}</li>
<li><strong>入职日期</strong>{{start_date}}</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🏢 公司信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>公司名称</strong>{company_name}</li>
<li><strong>公司地址</strong>{company_address}</li>
<li><strong>联系电话</strong>{company_phone}</li>
<li><strong>公司名称</strong>{{company_name}}</li>
<li><strong>公司地址</strong>{{company_address}}</li>
<li><strong>联系电话</strong>{{company_phone}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📋 入职安排</h3>
<p>{onboarding_schedule}</p>
<p>{{onboarding_schedule}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔑 系统访问信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>邮箱</strong>{email_address}</li>
<li><strong>初始密码</strong>{initial_password}</li>
<li><strong>系统登录地址</strong><a href="{system_login_url}" style="color: #007bff;">{system_login_url}</a></li>
<li><strong>邮箱</strong>{{email_address}}</li>
<li><strong>初始密码</strong>{{initial_password}}</li>
<li><strong>系统登录地址</strong><a href="{{system_login_url}}" style="color: #007bff;">{{system_login_url}}</a></li>
</ul>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">📚 重要资源</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>员工手册</strong><a href="{employee_handbook_url}" style="color: #007bff;">{employee_handbook_url}</a></li>
<li><strong>培训材料</strong><a href="{training_materials_url}" style="color: #007bff;">{training_materials_url}</a></li>
<li><strong>公司政策</strong><a href="{company_policies_url}" style="color: #007bff;">{company_policies_url}</a></li>
<li><strong>员工手册</strong><a href="{{employee_handbook_url}}" style="color: #007bff;">{{employee_handbook_url}}</a></li>
<li><strong>培训材料</strong><a href="{{training_materials_url}}" style="color: #007bff;">{{training_materials_url}}</a></li>
<li><strong>公司政策</strong><a href="{{company_policies_url}}" style="color: #007bff;">{{company_policies_url}}</a></li>
</ul>
</div>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👥 联系人</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>直属经理</strong>{manager_name} ({manager_email})</li>
<li><strong>HR联系人</strong>{hr_contact_name} ({hr_contact_email})</li>
<li><strong>IT支持</strong>{it_support_email}</li>
<li><strong>直属经理</strong>{{manager_name}} ({{manager_email}})</li>
<li><strong>HR联系人</strong>{{hr_contact_name}} ({{hr_contact_email}})</li>
<li><strong>IT支持</strong>{{it_support_email}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">🎯 第一周安排</h3>
<p>{first_week_schedule}</p>
<p>{{first_week_schedule}}</p>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
@ -590,7 +588,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>再次欢迎您的加入<br>{company_name} 团队</p>
<p>再次欢迎您的加入<br>{{company_name}} 团队</p>
</div>
</div>"""
}
@ -599,16 +597,16 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "welcome_email",
"region": 0,
"subject": "Welcome to {company_name} - {new_employee_name}",
"subject": "Welcome to {{company_name}} - {{new_employee_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {new_employee_name},</h2>
<h2 style="color: #333;">Dear {{new_employee_name}},</h2>
<div style="background-color: #d4edda; padding: 20px; border-radius: 5px; margin: 20px 0; text-align: center;">
<h1 style="color: #155724; margin: 0;">🎉 Welcome to {company_name}!</h1>
<h1 style="color: #155724; margin: 0;">🎉 Welcome to {{company_name}}!</h1>
</div>
<p>We are pleased to inform you that you have successfully joined our team. Here is your onboarding information:</p>
@ -616,58 +614,58 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 Employee Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Name: </strong>{new_employee_name}</li>
<li><strong>Employee ID: </strong>{employee_id}</li>
<li><strong>Department: </strong>{department}</li>
<li><strong>Position: </strong>{position}</li>
<li><strong>Start Date: </strong>{start_date}</li>
<li><strong>Name: </strong>{{new_employee_name}}</li>
<li><strong>Employee ID: </strong>{{employee_id}}</li>
<li><strong>Department: </strong>{{department}}</li>
<li><strong>Position: </strong>{{position}}</li>
<li><strong>Start Date: </strong>{{start_date}}</li>
</ul>
</div>
<div style="background-color: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🏢 Company Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Company Name: </strong>{company_name}</li>
<li><strong>Company Address: </strong>{company_address}</li>
<li><strong>Contact Phone: </strong>{company_phone}</li>
<li><strong>Company Name: </strong>{{company_name}}</li>
<li><strong>Company Address: </strong>{{company_address}}</li>
<li><strong>Contact Phone: </strong>{{company_phone}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📋 Onboarding Schedule</h3>
<p>{onboarding_schedule}</p>
<p>{{onboarding_schedule}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔑 System Access Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Email: </strong>{email_address}</li>
<li><strong>Initial Password: </strong>{initial_password}</li>
<li><strong>System Login URL: </strong><a href="{system_login_url}" style="color: #007bff;">{system_login_url}</a></li>
<li><strong>Email: </strong>{{email_address}}</li>
<li><strong>Initial Password: </strong>{{initial_password}}</li>
<li><strong>System Login URL: </strong><a href="{{system_login_url}}" style="color: #007bff;">{{system_login_url}}</a></li>
</ul>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;">📚 Important Resources</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Employee Handbook: </strong><a href="{employee_handbook_url}" style="color: #007bff;">{employee_handbook_url}</a></li>
<li><strong>Training Materials: </strong><a href="{training_materials_url}" style="color: #007bff;">{training_materials_url}</a></li>
<li><strong>Company Policies: </strong><a href="{company_policies_url}" style="color: #007bff;">{company_policies_url}</a></li>
<li><strong>Employee Handbook: </strong><a href="{{employee_handbook_url}}" style="color: #007bff;">{{employee_handbook_url}}</a></li>
<li><strong>Training Materials: </strong><a href="{{training_materials_url}}" style="color: #007bff;">{{training_materials_url}}</a></li>
<li><strong>Company Policies: </strong><a href="{{company_policies_url}}" style="color: #007bff;">{{company_policies_url}}</a></li>
</ul>
</div>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👥 Contacts</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Direct Manager: </strong>{manager_name} ({manager_email})</li>
<li><strong>HR Contact: </strong>{hr_contact_name} ({hr_contact_email})</li>
<li><strong>IT Support: </strong>{it_support_email}</li>
<li><strong>Direct Manager: </strong>{{manager_name}} ({{manager_email}})</li>
<li><strong>HR Contact: </strong>{{hr_contact_name}} ({{hr_contact_email}})</li>
<li><strong>IT Support: </strong>{{it_support_email}}</li>
</ul>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">🎯 First Week Schedule</h3>
<p>{first_week_schedule}</p>
<p>{{first_week_schedule}}</p>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
@ -675,7 +673,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Welcome aboard!<br>{company_name} Team</p>
<p>Welcome aboard!<br>{{company_name}} Team</p>
</div>
</div>"""
}
@ -693,36 +691,36 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "password_reset_email",
"region": 1,
"subject": "密码重置请求 - {user_name}",
"subject": "密码重置请求 - {{user_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">尊敬的 {user_name}</h2>
<h2 style="color: #333;">尊敬的 {{user_name}}</h2>
<p>我们收到了您的密码重置请求</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🔐 重置信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>用户名</strong>{user_name}</li>
<li><strong>邮箱</strong>{email_address}</li>
<li><strong>请求时间</strong>{request_time}</li>
<li><strong>请求IP</strong>{request_ip}</li>
<li><strong>用户名</strong>{{user_name}}</li>
<li><strong>邮箱</strong>{{email_address}}</li>
<li><strong>请求时间</strong>{{request_time}}</li>
<li><strong>请求IP</strong>{{request_ip}}</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔗 重置链接</h3>
<p><a href="{reset_link}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">点击重置密码</a></p>
<p style="font-size: 12px; color: #666;">或复制链接{reset_link}</p>
<p><a href="{{reset_link}}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">点击重置密码</a></p>
<p style="font-size: 12px; color: #666;">或复制链接{{reset_link}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> 重要提醒</h3>
<ul style="list-style: none; padding: 0;">
<li> 此链接将在 {expiry_hours} 小时后失效</li>
<li> 此链接将在 {{expiry_hours}} 小时后失效</li>
<li> 请勿将此链接分享给他人</li>
<li> 如果您没有请求重置密码请忽略此邮件</li>
</ul>
@ -730,7 +728,7 @@ class GlobalTemplateCreator:
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📱 验证码</h3>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{verification_code}</p>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{{verification_code}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
@ -744,12 +742,12 @@ class GlobalTemplateCreator:
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>如有任何问题请联系技术支持</p>
<p><strong>邮箱</strong>{support_email}</p>
<p><strong>电话</strong>{support_phone}</p>
<p><strong>邮箱</strong>{{support_email}}</p>
<p><strong>电话</strong>{{support_phone}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>谢谢<br>{company_name} 技术支持团队</p>
<p>谢谢<br>{{company_name}} 技术支持团队</p>
</div>
</div>"""
}
@ -758,36 +756,36 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "password_reset_email",
"region": 0,
"subject": "Password Reset Request - {user_name}",
"subject": "Password Reset Request - {{user_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {user_name},</h2>
<h2 style="color: #333;">Dear {{user_name}},</h2>
<p>We have received your password reset request.</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">🔐 Reset Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Username: </strong>{user_name}</li>
<li><strong>Email: </strong>{email_address}</li>
<li><strong>Request Time: </strong>{request_time}</li>
<li><strong>Request IP: </strong>{request_ip}</li>
<li><strong>Username: </strong>{{user_name}}</li>
<li><strong>Email: </strong>{{email_address}}</li>
<li><strong>Request Time: </strong>{{request_time}}</li>
<li><strong>Request IP: </strong>{{request_ip}}</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔗 Reset Link</h3>
<p><a href="{reset_link}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">Click to Reset Password</a></p>
<p style="font-size: 12px; color: #666;">Or copy the link: {reset_link}</p>
<p><a href="{{reset_link}}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">Click to Reset Password</a></p>
<p style="font-size: 12px; color: #666;">Or copy the link: {{reset_link}}</p>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;"> Important Reminder</h3>
<ul style="list-style: none; padding: 0;">
<li> This link will expire in {expiry_hours} hours</li>
<li> This link will expire in {{expiry_hours}} hours</li>
<li> Please do not share this link with others</li>
<li> If you did not request a password reset, please ignore this email</li>
</ul>
@ -795,7 +793,7 @@ class GlobalTemplateCreator:
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📱 Verification Code</h3>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{verification_code}</p>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{{verification_code}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
@ -809,12 +807,12 @@ class GlobalTemplateCreator:
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
<p>If you have any questions, please contact technical support:</p>
<p><strong>Email: </strong>{support_email}</p>
<p><strong>Phone: </strong>{support_phone}</p>
<p><strong>Email: </strong>{{support_email}}</p>
<p><strong>Phone: </strong>{{support_phone}}</p>
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Thank you!<br>{company_name} Technical Support Team</p>
<p>Thank you!<br>{{company_name}} Technical Support Team</p>
</div>
</div>"""
}
@ -832,41 +830,41 @@ class GlobalTemplateCreator:
template_data_cn = {
"template_id": "account_verification_email",
"region": 1,
"subject": "账号验证 - {user_name}",
"subject": "账号验证 - {{user_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">尊敬的 {user_name}</h2>
<h2 style="color: #333;">尊敬的 {{user_name}}</h2>
<p>感谢您注册 {company_name} 的账户请验证您的邮箱地址以完成注册</p>
<p>感谢您注册 {{company_name}} 的账户请验证您的邮箱地址以完成注册</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 账户信息</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>用户名</strong>{user_name}</li>
<li><strong>邮箱地址</strong>{email_address}</li>
<li><strong>注册时间</strong>{registration_time}</li>
<li><strong>用户名</strong>{{user_name}}</li>
<li><strong>邮箱地址</strong>{{email_address}}</li>
<li><strong>注册时间</strong>{{registration_time}}</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔗 验证链接</h3>
<p><a href="{verification_link}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">点击验证邮箱</a></p>
<p style="font-size: 12px; color: #666;">或复制链接{verification_link}</p>
<p><a href="{{verification_link}}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">点击验证邮箱</a></p>
<p style="font-size: 12px; color: #666;">或复制链接{{verification_link}}</p>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📱 验证码</h3>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{verification_code}</p>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{{verification_code}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;"> 验证期限</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>验证链接有效期</strong>{expiry_hours} 小时</li>
<li><strong>请在 {expiry_time} 前完成验证</strong></li>
<li><strong>验证链接有效期</strong>{{expiry_hours}} 小时</li>
<li><strong>请在 {{expiry_time}} 前完成验证</strong></li>
</ul>
</div>
@ -874,7 +872,7 @@ class GlobalTemplateCreator:
<h3 style="color: #155724; margin-top: 0;"> 验证步骤</h3>
<ol style="padding-left: 20px;">
<li>点击上面的验证链接</li>
<li>在登录页面输入验证码<strong>{verification_code}</strong></li>
<li>在登录页面输入验证码<strong>{{verification_code}}</strong></li>
</ol>
</div>
@ -890,8 +888,8 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📞 需要帮助</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>技术支持</strong>{support_email}</li>
<li><strong>客服热线</strong>{support_phone}</li>
<li><strong>技术支持</strong>{{support_email}}</li>
<li><strong>客服热线</strong>{{support_phone}}</li>
</ul>
</div>
@ -905,7 +903,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>谢谢<br>{company_name} 团队</p>
<p>谢谢<br>{{company_name}} 团队</p>
</div>
</div>"""
}
@ -914,41 +912,41 @@ class GlobalTemplateCreator:
template_data_en = {
"template_id": "account_verification_email",
"region": 0,
"subject": "Account Verification - {user_name}",
"subject": "Account Verification - {{user_name}}",
"body": """<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{company_logo}" alt="{company_name} Logo" style="max-height: 60px;">
<img src="{{company_logo}}" alt="{{company_name}} Logo" style="max-height: 60px;">
</div>
<h2 style="color: #333;">Dear {user_name},</h2>
<h2 style="color: #333;">Dear {{user_name}},</h2>
<p>Thank you for registering an account with {company_name}. Please verify your email address to complete your registration.</p>
<p>Thank you for registering an account with {{company_name}}. Please verify your email address to complete your registration.</p>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">👤 Account Information</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Username: </strong>{user_name}</li>
<li><strong>Email Address: </strong>{email_address}</li>
<li><strong>Registration Time: </strong>{registration_time}</li>
<li><strong>Username: </strong>{{user_name}}</li>
<li><strong>Email Address: </strong>{{email_address}}</li>
<li><strong>Registration Time: </strong>{{registration_time}}</li>
</ul>
</div>
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #856404; margin-top: 0;">🔗 Verification Link</h3>
<p><a href="{verification_link}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">Click to Verify Email</a></p>
<p style="font-size: 12px; color: #666;">Or copy the link: {verification_link}</p>
<p><a href="{{verification_link}}" style="color: #007bff; text-decoration: none; padding: 10px 20px; background-color: #007bff; color: white; border-radius: 5px; display: inline-block;">Click to Verify Email</a></p>
<p style="font-size: 12px; color: #666;">Or copy the link: {{verification_link}}</p>
</div>
<div style="background-color: #d4edda; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #155724; margin-top: 0;">📱 Verification Code</h3>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{verification_code}</p>
<p style="font-size: 24px; font-weight: bold; text-align: center; letter-spacing: 5px; color: #155724;">{{verification_code}}</p>
</div>
<div style="background-color: #cce5ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #004085; margin-top: 0;"> Verification Period</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Verification Link Valid: </strong>{expiry_hours} hours</li>
<li><strong>Please complete verification before {expiry_time}</strong></li>
<li><strong>Verification Link Valid: </strong>{{expiry_hours}} hours</li>
<li><strong>Please complete verification before {{expiry_time}}</strong></li>
</ul>
</div>
@ -956,7 +954,7 @@ class GlobalTemplateCreator:
<h3 style="color: #155724; margin-top: 0;"> Verification Steps</h3>
<ol style="padding-left: 20px;">
<li>Click the verification link above, or</li>
<li>Enter the verification code on the login page: <strong>{verification_code}</strong></li>
<li>Enter the verification code on the login page: <strong>{{verification_code}}</strong></li>
</ol>
</div>
@ -972,8 +970,8 @@ class GlobalTemplateCreator:
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h3 style="color: #007bff; margin-top: 0;">📞 Need Help?</h3>
<ul style="list-style: none; padding: 0;">
<li><strong>Technical Support: </strong>{support_email}</li>
<li><strong>Customer Service: </strong>{support_phone}</li>
<li><strong>Technical Support: </strong>{{support_email}}</li>
<li><strong>Customer Service: </strong>{{support_phone}}</li>
</ul>
</div>
@ -987,7 +985,7 @@ class GlobalTemplateCreator:
</div>
<div style="text-align: center; margin-top: 30px; color: #666; font-size: 12px;">
<p>Thank you!<br>{company_name} Team</p>
<p>Thank you!<br>{{company_name}} Team</p>
</div>
</div>"""
}

View File

@ -139,18 +139,12 @@ async def create_global_template(request: TemplateCreateRequest):
body=request.body,
is_active=request.is_active
)
# check if the template is created or skipped (already exists)
is_skipped = hasattr(result, '_is_existing') and result._is_existing
return JSONResponse(
content={
"message": f"Global template {'skipped (already exists)' if is_skipped else 'created'} successfully",
"template_id": request.template_id,
"action": "skipped" if is_skipped else "created"
},
status_code=200 if is_skipped else 201
content={"message": "Global template created successfully", "template_id": request.template_id},
status_code=201
)
except ValueError as e:
# duplicate or validation error
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
import traceback
@ -286,37 +280,19 @@ async def create_tenant_template(request: TemplateCreateRequest, payload: dict =
tenant_id=tenant_id,
is_active=request.is_active
)
# check if the template is created or skipped (already exists)
is_skipped = hasattr(result, '_is_existing') and result._is_existing
# normalize minimal response
if hasattr(result, 'dict'):
result_dict = result.dict()
for key, value in result_dict.items():
if hasattr(value, 'isoformat'):
result_dict[key] = value.isoformat()
elif hasattr(value, 'value'):
result_dict[key] = value.value
else:
result_dict = {
"template_id": getattr(result, 'template_id', request.template_id),
"tenant_id": getattr(result, 'tenant_id', tenant_id),
"region": getattr(result, 'region', request.region),
"subject": getattr(result, 'subject', request.subject),
"body": getattr(result, 'body', request.body),
"is_active": getattr(result, 'is_active', request.is_active)
}
result_dict = {"template_id": request.template_id, "tenant_id": tenant_id, "region": request.region}
return JSONResponse(
content={
"message": f"Tenant template {'skipped (already exists)' if is_skipped else 'created'} successfully",
"result": result_dict,
"action": "skipped" if is_skipped else "created"
},
status_code=200 if is_skipped else 201,
content={"message": "Tenant template created successfully", "result": result_dict},
status_code=201,
media_type="application/json"
)
except ValueError as e:
# duplicate or validation error
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
import traceback