feat: Add Gitea username integration, refactor password hashing with direct bcrypt, and remove frontend version displays.

This commit is contained in:
vinland100 2026-01-05 13:47:48 +08:00
parent bc1597a41d
commit 4d3761e0e0
9 changed files with 96 additions and 21 deletions

View File

@ -0,0 +1,34 @@
"""add gitea_username to user model
Revision ID: 08a73307418d
Revises: ecc7c0ff0957
Create Date: 2026-01-05 13:36:16.845876
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '08a73307418d'
down_revision = 'ecc7c0ff0957'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('gitea_username', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'gitea_username')
# ### end Alembic commands ###

View File

@ -1,18 +1,10 @@
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Any, Union from typing import Any, Union
from jose import jwt from jose import jwt
import bcrypt # Import first import bcrypt
# MonkeyPatch passlib/bcrypt compatibility (passlib expects __about__)
if not hasattr(bcrypt, "__about__"):
from types import SimpleNamespace
bcrypt.__about__ = SimpleNamespace(__version__=bcrypt.__version__)
from passlib.context import CryptContext
from app.core.config import settings from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ALGORITHM = settings.ALGORITHM ALGORITHM = settings.ALGORITHM
def create_access_token( def create_access_token(
@ -29,10 +21,36 @@ def create_access_token(
return encoded_jwt return encoded_jwt
def verify_password(plain_password: str, hashed_password: str) -> bool: def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password) """
Verify a password against a hash.
Explicitly truncate to 72 bytes to avoid bcrypt ValueError and maintain compatibility.
"""
if not plain_password or not hashed_password:
return False
try:
password_bytes = plain_password.encode("utf-8")
if len(password_bytes) > 72:
password_bytes = password_bytes[:72]
return bcrypt.checkpw(
password_bytes,
hashed_password.encode("utf-8")
)
except Exception:
return False
def get_password_hash(password: str) -> str: def get_password_hash(password: str) -> str:
return pwd_context.hash(password) """
Generate a bcrypt hash of the password.
Explicitly truncate to 72 bytes for consistency.
"""
password_bytes = password.encode("utf-8")
if len(password_bytes) > 72:
password_bytes = password_bytes[:72]
salt = bcrypt.gensalt()
return bcrypt.hashpw(password_bytes, salt).decode("utf-8")

View File

@ -19,6 +19,7 @@ class User(Base):
role = Column(String, default="member") role = Column(String, default="member")
github_username = Column(String, nullable=True) github_username = Column(String, nullable=True)
gitlab_username = Column(String, nullable=True) gitlab_username = Column(String, nullable=True)
gitea_username = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now()) created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now())

View File

@ -13,6 +13,7 @@ class UserBase(BaseModel):
role: str = "member" role: str = "member"
github_username: Optional[str] = None github_username: Optional[str] = None
gitlab_username: Optional[str] = None gitlab_username: Optional[str] = None
gitea_username: Optional[str] = None
class UserCreate(UserBase): class UserCreate(UserBase):
email: EmailStr email: EmailStr

View File

@ -275,8 +275,8 @@ SYSTEM_RULE_SETS = [
"description": "基于 OWASP Top 10 2021 的安全审计规则集", "description": "基于 OWASP Top 10 2021 的安全审计规则集",
"language": "all", "language": "all",
"rule_type": "security", "rule_type": "security",
"is_default": True, "is_default": False,
"sort_order": 0, "sort_order": 1,
"severity_weights": {"critical": 10, "high": 5, "medium": 2, "low": 1}, "severity_weights": {"critical": 10, "high": 5, "medium": 2, "low": 1},
"rules": [ "rules": [
{ {
@ -386,8 +386,8 @@ SYSTEM_RULE_SETS = [
"description": "通用代码质量检查规则集", "description": "通用代码质量检查规则集",
"language": "all", "language": "all",
"rule_type": "quality", "rule_type": "quality",
"is_default": False, "is_default": True,
"sort_order": 1, "sort_order": 0,
"severity_weights": {"critical": 10, "high": 5, "medium": 2, "low": 1}, "severity_weights": {"critical": 10, "high": 5, "medium": 2, "low": 1},
"rules": [ "rules": [
{ {
@ -550,6 +550,11 @@ async def init_system_templates(db: AsyncSession) -> None:
) )
db.add(template) db.add(template)
logger.info(f"✓ 创建系统提示词模板: {template_data['name']}") logger.info(f"✓ 创建系统提示词模板: {template_data['name']}")
else:
# 更新已存在的系统模板的默认状态和排序
existing.is_default = template_data.get("is_default", False)
existing.sort_order = template_data.get("sort_order", 0)
db.add(existing)
await db.flush() await db.flush()
@ -599,6 +604,11 @@ async def init_system_rule_sets(db: AsyncSession) -> None:
db.add(rule) db.add(rule)
logger.info(f"✓ 创建系统规则集: {rule_set_data['name']} ({len(rule_set_data.get('rules', []))} 条规则)") logger.info(f"✓ 创建系统规则集: {rule_set_data['name']} ({len(rule_set_data.get('rules', []))} 条规则)")
else:
# 更新已存在的系统规则集的默认状态和排序
existing.is_default = rule_set_data.get("is_default", False)
existing.sort_order = rule_set_data.get("sort_order", 0)
db.add(existing)
await db.flush() await db.flush()

View File

@ -467,7 +467,6 @@ export default function TerminalProgressDialog({
<Terminal className="w-5 h-5 text-primary" /> <Terminal className="w-5 h-5 text-primary" />
<div> <div>
<span className="text-lg font-bold uppercase tracking-[0.15em] text-slate-800 dark:text-[#f0e6d3]">AUDIT_TERMINAL</span> <span className="text-lg font-bold uppercase tracking-[0.15em] text-slate-800 dark:text-[#f0e6d3]">AUDIT_TERMINAL</span>
<span className="text-xs text-slate-500 dark:text-[#5a6577] ml-2 tracking-wider">v3.0</span>
</div> </div>
</div> </div>

View File

@ -38,6 +38,7 @@ export default function Account() {
phone: "", phone: "",
github_username: "", github_username: "",
gitlab_username: "", gitlab_username: "",
gitea_username: "",
}); });
const [passwordForm, setPasswordForm] = useState({ const [passwordForm, setPasswordForm] = useState({
current_password: "", current_password: "",
@ -60,6 +61,7 @@ export default function Account() {
phone: res.data.phone || "", phone: res.data.phone || "",
github_username: res.data.github_username || "", github_username: res.data.github_username || "",
gitlab_username: res.data.gitlab_username || "", gitlab_username: res.data.gitlab_username || "",
gitea_username: res.data.gitea_username || "",
}); });
} catch (error) { } catch (error) {
console.error('Failed to load profile:', error); console.error('Failed to load profile:', error);
@ -288,6 +290,18 @@ export default function Account() {
className="cyber-input" className="cyber-input"
/> />
</div> </div>
<div className="space-y-2">
<Label htmlFor="gitea" className="text-xs font-bold text-muted-foreground uppercase flex items-center gap-2">
<GitBranch className="w-3 h-3" /> Gitea
</Label>
<Input
id="gitea"
value={form.gitea_username}
onChange={(e) => setForm({ ...form, gitea_username: e.target.value })}
placeholder="your-gitea-username"
className="cyber-input"
/>
</div>
</div> </div>
</div> </div>

View File

@ -15,7 +15,7 @@ interface SplashScreenProps {
// Enhanced boot sequence messages with icons // Enhanced boot sequence messages with icons
const BOOT_SEQUENCE = [ const BOOT_SEQUENCE = [
{ text: "[INIT] Loading DeepAudit Core...", delay: 0, type: 'init' }, { text: "[INIT] Loading DeepAudit Core...", delay: 0, type: 'init' },
{ text: "[SCAN] AI Code Review Engine v3.0", delay: 200, type: 'scan' }, { text: "[SCAN] AI Code Review Engine", delay: 200, type: 'scan' },
{ text: "[LOAD] Vulnerability Pattern Database", delay: 400, type: 'load' }, { text: "[LOAD] Vulnerability Pattern Database", delay: 400, type: 'load' },
{ text: "[SYNC] Agent Orchestration Module", delay: 600, type: 'sync' }, { text: "[SYNC] Agent Orchestration Module", delay: 600, type: 'sync' },
{ text: "[READY] System Online", delay: 800, type: 'ready' }, { text: "[READY] System Online", delay: 800, type: 'ready' },
@ -234,10 +234,7 @@ export function SplashScreen({ onComplete }: SplashScreenProps) {
<AppLogo size="xl" subtitle="AI Code Review Bot" /> <AppLogo size="xl" subtitle="AI Code Review Bot" />
</div> </div>
</div> </div>
{/* Version tag */} {/* Version tag removed */}
<div className="mt-2 text-[10px] font-mono text-primary/50 tracking-widest">
[ v3.0.0 // NEURAL_CORE ]
</div>
</div> </div>
{/* Terminal window - adaptive styling */} {/* Terminal window - adaptive styling */}

View File

@ -16,6 +16,7 @@ export interface Profile {
role: 'admin' | 'member'; role: 'admin' | 'member';
github_username?: string; github_username?: string;
gitlab_username?: string; gitlab_username?: string;
gitea_username?: string;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
} }