a
    h.c                     @   s  d dl mZmZmZmZ d dlmZ d dlZd dlZd dl	Z	d dl
Z
d dlmZ d dlZd dlZd dlZd dlmZ d dlmZ d dlmZ d dlmZ d d	lmZ d d
lmZ d dlZd dlZd dlZe
je
j d e
!e"Z#ee"Z$ee$ G dd dZ%e% Z&dd Z'G dd dZ(e( Z)e$j*ddgddd Z+e$j*ddgddd Z,e$j*ddgde'dd Z-e$j*ddgde'dd  Z.e$j*d!dgde'd"d# Z/e$j*d$dgde'd%d& Z0e$j*d'dgde'd(d) Z1e$j*d*dgdd+d, Z2e$j*d-dgde'd.d/ Z3e"d0kre4d1 e4d2 e4d1 e4d3 e4d4 e4d5 e4d6 e4d7 e4d8e&j5  e4d9 e4d7 e4d: e4d; e4d< e4d= e4d> e4d? e4d@ e4dA e4dB e4dC e4d7 e4dD e4d1 e$j6dEdFdGdH dS )I    )Flaskrequestjsonify	send_file)CORSN)datetimewraps)canvas)inch)
pdfmetrics)TTFont)Image)levelc                   @   s6   e Zd Zdd Zdd Zdd Zdd Zdd
dZdS )SecurityConfigc                 C   s0   t jdd| _d| _i | _g | _td d S )NZCERTIFICATE_API_KEYzcert2024!@#Z@75f4116161479933cf85b30bd37080b1f22168de9057da5ac934cc5aee8c59e7u   安全模块已初始化)	osenvirongetAPI_KEYAPI_KEY_HASHZactive_sessions
access_logloggerinfoself r   2/opt/app/certificate-api/certificate_api_secure.py__init__   s
    zSecurityConfig.__init__c                 C   s   t |  S )u   对密钥进行哈希处理)hashlibsha256encode	hexdigest)r   keyr   r   r   	_hash_key-   s    zSecurityConfig._hash_keyc                 C   s   |sdS |  || jkS )   验证API密钥F)r#   r   )r   Zprovided_keyr   r   r   verify_api_key1   s    zSecurityConfig.verify_api_keyc                 C   s
   t dS )u   生成会话令牌    )secretsZtoken_urlsafer   r   r   r   generate_session_token7   s    z%SecurityConfig.generate_session_tokenTc                 C   sD   t   |||d}| j| t| jdkr@| jdd | _dS )u   记录访问日志)	timestampipendpointsuccessi  iN)r   now	isoformatr   appendlen)r   r*   r+   r,   Z	log_entryr   r   r   
log_access;   s    
zSecurityConfig.log_accessN)T)__name__
__module____qualname__r   r#   r%   r(   r1   r   r   r   r   r      s
   r   c                    s   t   fdd}|S )u   API密钥认证装饰器c                     s   t j}t jd}d }|r2|dr2|dd  }n0t jrRt  }|rN|dnd }|sbt jd}t	|stj
|t jdd tddd	d
d	fS tj
|t jdd  | i |S )NAuthorizationzBearer    api_keyF)r,      无效的API密钥u'   请提供有效的API密钥进行访问  )errormessagecodeT)r   remote_addrheadersr   
startswithis_jsonget_jsonargssecurityr%   r1   r+   r   )rB   kwargsZ	client_ipZauth_headerr7   datafr   r   decorated_functionO   s*    
z(require_auth.<locals>.decorated_functionr   )rG   rH   r   rF   r   require_authM   s    rI   c                   @   s6   e Zd Zdd ZdddZdd Zdd	 Zd
d ZdS )CertificateAPIGeneratorc                 C   s   t tddr&tjtjtj| _ntjtjt| _tj	| jd| _
tj	| jd| _tj| j
dd tj| jdd d| _d| _d	| _d
| _d| _d| _d| _d| _d| _d| _d| _g d| _g d| _d S )NfrozenF	templatesfontsT)exist_okzdxai.jpgyumindb   i(  i  u   AIデータアナリストi  iJ    iT  )
u#   AIプログラマー養成コースu#   AIサーバー構築基礎コースu#   AIデータアナリストコースu   AIプログラミング実戦u    AIデータ分析基礎コースu)   AIデータサイエンス基礎コースu1   生成AI（活用、ChatGPT、Copilot）コースu.   生成AI（活用、ChatGPT、Copilot）講座u+   AIサーバーのDX化活用基礎コースu2   AIデータサイエンティスト育成コース)rO   ZmsgothicZmsmincho)getattrsysr   pathdirnameabspath
executable	base_path__file__jointemplates_dir	fonts_dirmakedirsdefault_templatedefault_fontdefault_font_sizedefault_name_ydefault_furigana_ydefault_courseZdefault_name_xdefault_number_xdefault_number_ydefault_date_xdefault_date_yavailable_coursesZavailable_fontsr   r   r   r   r   s   s(    
z CertificateAPIGenerator.__init__Nc                 K   s  z`| d| j}| d| j}t| d| j}| dd }| d| j}t| d| j}	t| d| j	}
|r| st
d	| }|d
 }tj| j|}tj|std| t|}|j\}}|du rtjddd}|j}|  tj|||fd}|||f | | |j|dd||ddd | |||||||||		 |  |W S  ty } z"t !dt"|   W Y d}~n
d}~0 0 dS )uT   
        生成证书的核心函数 - 完全按照原始GUI版本的逻辑
        templatefont	font_sizefurigana coursename_y
furigana_y   姓名不能为空   　殿u   模板文件不存在: NFz.pdf)deletesuffix)Zpagesizer   Tc)ZpreserveAspectRatioanchoru   生成证书时出错: )#r   r^   r_   intr`   striprc   floatra   rb   
ValueErrorr   rT   rZ   r[   existsFileNotFoundErrorr   opensizetempfileNamedTemporaryFilenamecloser
   ZCanvasZsetPageSize_load_fonts_originalZ	drawImage_draw_certificate_originalsave	Exceptionr   r:   str)r   r   output_pathrD   ri   rj   rk   rl   rn   ro   rp   Ztemplate_pathZimgwidthheightZ	temp_fileru   er   r   r   create_certificate   sJ    


	z*CertificateAPIGenerator.create_certificatec              
   C   s  zt j| jstdt j| jd}t j|r`zttd| W qh   tdY qh0 ntd|dkr| d}t j| j|}t j|rztt|| W q   td| dY q0 ntd	| d
W n< ty } z"t	
dt|   W Y d}~n
d}~0 0 dS )u'   按原始代码逻辑加载字体文件uC   找不到fonts文件夹，请确保fonts文件夹在程序目录中zyumindb.ttfrO   uA   默认字体文件加载失败，请确保yumindb.ttf文件完整uR   找不到默认字体文件yumindb.ttf，请确保字体文件在fonts文件夹中.ttfu   字体文件 u+    加载失败，请确保字体文件完整u   找不到字体文件 u,   ，请确保字体文件在fonts文件夹中u   加载字体失败: N)r   rT   r{   r\   r   rZ   r   ZregisterFontr   r   r:   r   )r   rj   Zdefault_font_pathZ	font_fileZ	font_pathr   r   r   r   r      s,    
z,CertificateAPIGenerator._load_fonts_originalc
           (      C   s  d}
|D ]*}t |dk r&|
|d 7 }
q|
|d 7 }
q|d d }||
d  }||| d}||| |	| ||| |	| |||	| |rt|d }|d	| ||}||d  }|	d
 }d}|dd}| }| }t|t|kr|}tt	||D ]\}\}}||||}||d	|}||| d  }||| || ||| || |||| |t|d k r|||d  7 }q|
ddd t|d }|d	| ||}|d |d  }d}d}||| || ||| || |||| |
ddd |d	d | j} | j}!| |}"d}#|| |!|#|"  | j}$| j}%t d}&d}'||$|%|'|&  dS )u'   按原始代码逻辑绘制整个证书r      g      ?g      ?      g?gGz?rO   d   g333333?rr   rm      gp=
ף?gRQ?g?g?i  <   u   証明書番号：z%Y/%m/%du   発行日：N)ordZsetFontZ
drawStringrw   ZstringWidthreplacesplitr0   	enumeratezipZsetFillColorRGBrd   re   generate_certificate_numberrf   rg   r   r-   strftime)(r   ru   r   Zselected_fontZname_font_sizerl   Zcourse_textr   r   ro   Ztotal_widthcharZcenter_xZ
adjusted_xoffsetZfurigana_sizeZfurigana_widthZ
furigana_xrp   Zoffset_smallZ	main_nameZ
name_partsZfurigana_partsZ	current_xiZ	name_partZfurigana_partZ
part_widthZcourse_font_sizeZcourse_widthZcourse_xZcourse_yZnumber_xZnumber_ycertificate_numberZ
cert_labelZdate_xZdate_yZcurrent_dateZ
date_labelr   r   r   r     sn    


z2CertificateAPIGenerator._draw_certificate_originalc                 C   s6   t  d}tt|d }|d}| | }|S )uh   
        生成11位证书编号 - 与原始代码一致
        格式: YYMMDD + 5位哈希值
        z%y%m%di Z05d)r   r-   r   abshash)r   r   Zdate_strZ	name_hashZname_hash_strr   r   r   r   r   m  s
    z3CertificateAPIGenerator.generate_certificate_number)N)r2   r3   r4   r   r   r   r   r   r   r   r   r   rJ   r   s
   /
@!jrJ   /GET)methodsc                   C   s0   t ddddg ddddd	d
ddddddS )u-   API根路径，返回API信息和认证说明z"Certificate Generator API (Secure)z2.0.0u3   安全的PDF证书生成API - 需要API密钥认证zAPI Key)zAuthorization: Bearer <api_key>z#JSON body: {'api_key': '<api_key>'}z!URL parameter: ?api_key=<api_key>u*   所有API端点都需要有效的API密钥)typer   Znoteu   POST - 验证API密钥u(   POST - 生成单个证书 (需要认证)u(   POST - 批量生成证书 (需要认证)u'   GET - 列出可用模板 (需要认证)u'   GET - 列出可用字体 (需要认证)u'   GET - 列出可用课程 (需要认证)u!   GET - 健康检查 (无需认证))/auth	/generate/batch_generate
/templates/fonts/courses/health)r   versiondescriptionZauthenticationZ	endpoints)r   r   r   r   r   index  s"    
r   r   POSTc               
   C   s   z|t jrt  ni } | dp(t jd}|sBtddddfW S t|rftddt	 
 dW S td	d
ddfW S W n8 ty } z tdt|idfW  Y d}~S d}~0 0 dS )r$   r7   u   缺少API密钥u   请提供api_key参数)r:   r;   rQ   Tu   API密钥验证成功)r,   r;   r)   Fr8   )r,   r;   r9   r:     N)r   r@   rA   r   rB   r   rC   r%   r   r-   r.   r   r   )rE   r7   r   r   r   r   authenticate  s0    



r   r   c               
   C   s,  zt  } | s tddidfW S | d}|s@tddidfW S | dtj}| dtj}| dtj}| d	d
}| dtj}| dtj	}| dtj
}tj||||||||d}	t|	d|dd
 dddW S  ty& }
 z4tdt|
  tdt|
idfW  Y d}
~
S d}
~
0 0 dS )u-   生成单个证书的API端点 - 需要认证r:      请提供JSON数据rQ   r   rq   ri   rj   rk   rl   rm   rn   ro   rp   )r   ri   rj   rk   rl   rn   ro   rp   Trr      _证书.pdfzapplication/pdf)Zas_attachmentZdownload_nameZmimetypeu   生成证书失败: r   N)r   rA   r   r   	generatorr^   r_   r`   rc   ra   rb   r   r   r   r   r   r:   r   )rE   r   ri   rj   rk   rl   rn   ro   rp   r   r   r   r   r   generate_certificate  sB    
r   r   c                  C   sf  zt  } | s"tddidfW S | d}|r:t|tsLtddidfW S | dtj}| dtj}| dtj	}| d	tj
}g }d
}|D ]`}zt|tr|}	d}
|}nJt|tr|dd}	|dd}
|d	|}n|t|ddd W q|	s||	ddd W qtj|	||||
|d}t|d$}t| d}W d   n1 sd0    Y  t| ||	d||	dd dd |d7 }W q ty } z2|dt v r|	nddt|d W Y d}~qd}~0 0 qtt||t|| |dW S  ty` } z4tdt|  tdt|idfW  Y d}~S d}~0 0 dS )u-   批量生成证书的API端点 - 需要认证r:   r   rQ   namesu   请提供姓名列表ri   rj   rk   rn   r   rm   r   rl   u   无效的数据格式)r   statusr;   rq   )r   ri   rj   rk   rl   rn   rbzutf-8Nr,   rr   r   )r   r   Z
pdf_base64filenamer   unknown)totalr,   Zfailedresultsu   批量生成证书失败: r   )r   rA   r   r   
isinstancelistr   r^   r_   r`   rc   r   dictr/   r   r}   base64	b64encodereaddecoder   unlinkr   r   localsr0   r   r:   )rE   r   ri   rj   rk   rn   r   Zsuccess_countitemr   rl   Zitem_courser   rG   Zpdf_contentr   r   r   r   batch_generate_certificates  sz    




4
 

r   r   c               
   C   sz   z<g } t tjD ]}| dr| | qtd| iW S  tyt } z tdt	|idfW  Y d}~S d}~0 0 dS )u$   列出可用的模板 - 需要认证)z.jpgz.jpegz.pngrL   r:   r   N)
r   listdirr   r[   lowerendswithr/   r   r   r   )rL   filer   r   r   r   list_templates=  s    r   r   c               
   C   s   zDg } t tjD ]$}| dr| |dd qtd| iW S  t	y| } z tdt
|idfW  Y d}~S d}~0 0 dS )u$   列出可用的字体 - 需要认证r   rm   rM   r:   r   N)r   r   r   r\   r   r   r/   r   r   r   r   )rM   r   r   r   r   r   
list_fontsJ  s    r   r   c                   C   s   t dtjiS )u$   列出可用的课程 - 需要认证Zcourses)r   r   rh   r   r   r   r   list_coursesW  s    r   r   c                   C   s   t dt  ddS )u!   健康检查端点 - 无需认证ZhealthyZenabled)r   r)   rC   )r   r   r-   r.   r   r   r   r   health_check]  s
    
r   z/admin/logsc               
   C   sb   z$t jdd } ttt j| dW S  ty\ } z tdt|idfW  Y d}~S d}~0 0 dS )u!   获取访问日志 - 需要认证iN)Z
total_logsrecent_logsr:   r   )rC   r   r   r0   r   r   )r   r   r   r   r   get_access_logsf  s    
r   __main__z<============================================================u,   🔒 安全证书生成API服务启动中...u   安全特性:u   - ✅ API密钥认证u   - ✅ 访问日志记录u   - ✅ 安全哈希验证rm   u   默认API密钥: u5   ⚠️  部署到生产环境时请修改API密钥！u
   API端点:u   - GET  / : API信息u   - POST /auth : 验证密钥u4   - POST /generate : 生成单个证书 (需要认证)u:   - POST /batch_generate : 批量生成证书 (需要认证)u5   - GET  /templates : 列出可用模板 (需要认证)u1   - GET  /fonts : 列出可用字体 (需要认证)u3   - GET  /courses : 列出可用课程 (需要认证)u   - GET  /health : 健康检查u0   - GET  /admin/logs : 访问日志 (需要认证)u#   服务地址: http://localhost:5000Fz0.0.0.0i  )debughostport)7Zflaskr   r   r   r   Z
flask_corsr   r   rS   r   loggingr   randomr   r'   	functoolsr	   Zreportlab.pdfgenr
   Zreportlab.lib.unitsr   Zreportlab.pdfbaser   Zreportlab.pdfbase.ttfontsr   ZPILr   Zpandaspdior   basicConfigINFO	getLoggerr2   r   appr   rC   rI   rJ   r   Zrouter   r   r   r   r   r   r   r   r   printr   runr   r   r   r   <module>   s   
-%  

-R

