feat: add initial frontend setup with styles, types, and testing framework

- Created a global CSS file for styling the frontend with responsive design.
- Introduced TypeScript types for various entities including Research, Alpha, and Account.
- Implemented Playwright tests for account management and data synchronization workflows.
- Configured TypeScript with strict settings and included necessary libraries.
- Set up Vite as the build tool with React plugin and API proxy configuration.
- Added a Python script to initialize environment variables securely.
This commit is contained in:
yuxuanhui
2026-09-07 14:54:20 +08:00
commit 3cd280d068
53 changed files with 10888 additions and 0 deletions
@@ -0,0 +1,153 @@
"""Initial account alpha and durable job schema"""
from alembic import op
import sqlalchemy as sa
revision = '0001'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('accounts',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=254), nullable=True),
sa.Column('password_encrypted', sa.Text(), nullable=True),
sa.Column('wq_user_id', sa.String(length=100), nullable=True),
sa.Column('profile', sa.JSON(), nullable=False),
sa.Column('connection_status', sa.String(length=30), nullable=False),
sa.Column('connection_error', sa.Text(), nullable=True),
sa.Column('verification_url', sa.Text(), nullable=True),
sa.Column('last_synced_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('display_name', sa.String(length=100), nullable=False),
sa.Column('theme', sa.String(length=10), nullable=False),
sa.Column('timezone', sa.String(length=64), nullable=False),
sa.Column('page_size', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_table('admins',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=100), nullable=False),
sa.Column('password_hash', sa.Text(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username')
)
op.create_table('alphas',
sa.Column('id', sa.String(length=100), nullable=False),
sa.Column('name', sa.Text(), nullable=True),
sa.Column('expression', sa.Text(), nullable=True),
sa.Column('selection', sa.Text(), nullable=True),
sa.Column('combo', sa.Text(), nullable=True),
sa.Column('alpha_type', sa.String(length=50), nullable=True),
sa.Column('language', sa.String(length=50), nullable=True),
sa.Column('stage', sa.String(length=50), nullable=True),
sa.Column('status', sa.String(length=80), nullable=True),
sa.Column('hidden', sa.Boolean(), nullable=False),
sa.Column('region', sa.String(length=50), nullable=True),
sa.Column('universe', sa.String(length=100), nullable=True),
sa.Column('settings', sa.JSON(), nullable=False),
sa.Column('is_metrics', sa.JSON(), nullable=False),
sa.Column('os_metrics', sa.JSON(), nullable=False),
sa.Column('checks', sa.JSON(), nullable=False),
sa.Column('sharpe', sa.Float(), nullable=True),
sa.Column('fitness', sa.Float(), nullable=True),
sa.Column('returns', sa.Float(), nullable=True),
sa.Column('turnover', sa.Float(), nullable=True),
sa.Column('margin', sa.Float(), nullable=True),
sa.Column('drawdown', sa.Float(), nullable=True),
sa.Column('date_created', sa.DateTime(timezone=True), nullable=True),
sa.Column('date_submitted', sa.DateTime(timezone=True), nullable=True),
sa.Column('synced_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('raw', sa.JSON(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_alphas_alpha_type'), 'alphas', ['alpha_type'], unique=False)
op.create_index(op.f('ix_alphas_date_created'), 'alphas', ['date_created'], unique=False)
op.create_index(op.f('ix_alphas_fitness'), 'alphas', ['fitness'], unique=False)
op.create_index(op.f('ix_alphas_region'), 'alphas', ['region'], unique=False)
op.create_index('ix_alphas_region_status', 'alphas', ['region', 'status'], unique=False)
op.create_index(op.f('ix_alphas_sharpe'), 'alphas', ['sharpe'], unique=False)
op.create_index(op.f('ix_alphas_stage'), 'alphas', ['stage'], unique=False)
op.create_index(op.f('ix_alphas_status'), 'alphas', ['status'], unique=False)
op.create_table('login_sessions',
sa.Column('token_hash', sa.String(length=64), nullable=False),
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('token_hash')
)
op.create_index(op.f('ix_login_sessions_expires_at'), 'login_sessions', ['expires_at'], unique=False)
op.create_table('sync_jobs',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('kind', sa.String(length=30), nullable=False),
sa.Column('status', sa.String(length=30), nullable=False),
sa.Column('payload', sa.JSON(), nullable=False),
sa.Column('checkpoint', sa.JSON(), nullable=False),
sa.Column('processed', sa.Integer(), nullable=False),
sa.Column('failed', sa.Integer(), nullable=False),
sa.Column('total', sa.Integer(), nullable=True),
sa.Column('error', sa.Text(), nullable=True),
sa.Column('next_retry_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('cancel_requested', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_sync_jobs_status'), 'sync_jobs', ['status'], unique=False)
op.create_table('job_items',
sa.Column('job_id', sa.String(length=36), nullable=False),
sa.Column('alpha_id', sa.String(length=100), nullable=False),
sa.Column('error', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['job_id'], ['sync_jobs.id'], ),
sa.PrimaryKeyConstraint('job_id', 'alpha_id')
)
op.create_table('pnl_cache',
sa.Column('alpha_id', sa.String(length=100), nullable=False),
sa.Column('raw', sa.JSON(), nullable=False),
sa.Column('points', sa.JSON(), nullable=False),
sa.Column('fetched_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['alpha_id'], ['alphas.id'], ),
sa.PrimaryKeyConstraint('alpha_id')
)
op.create_table('research',
sa.Column('alpha_id', sa.String(length=100), nullable=False),
sa.Column('note', sa.Text(), nullable=False),
sa.Column('tags', sa.JSON(), nullable=False),
sa.Column('favorite', sa.Boolean(), nullable=False),
sa.Column('state', sa.String(length=30), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['alpha_id'], ['alphas.id'], ),
sa.PrimaryKeyConstraint('alpha_id')
)
op.create_index(op.f('ix_research_state'), 'research', ['state'], unique=False)
op.create_table('research_tags',
sa.Column('alpha_id', sa.String(length=100), nullable=False),
sa.Column('tag', sa.String(length=60), nullable=False),
sa.ForeignKeyConstraint(['alpha_id'], ['alphas.id'], ),
sa.PrimaryKeyConstraint('alpha_id', 'tag')
)
op.create_index(op.f('ix_research_tags_tag'), 'research_tags', ['tag'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_research_tags_tag'), table_name='research_tags')
op.drop_table('research_tags')
op.drop_index(op.f('ix_research_state'), table_name='research')
op.drop_table('research')
op.drop_table('pnl_cache')
op.drop_table('job_items')
op.drop_index(op.f('ix_sync_jobs_status'), table_name='sync_jobs')
op.drop_table('sync_jobs')
op.drop_index(op.f('ix_login_sessions_expires_at'), table_name='login_sessions')
op.drop_table('login_sessions')
op.drop_index(op.f('ix_alphas_status'), table_name='alphas')
op.drop_index(op.f('ix_alphas_stage'), table_name='alphas')
op.drop_index(op.f('ix_alphas_sharpe'), table_name='alphas')
op.drop_index('ix_alphas_region_status', table_name='alphas')
op.drop_index(op.f('ix_alphas_region'), table_name='alphas')
op.drop_index(op.f('ix_alphas_fitness'), table_name='alphas')
op.drop_index(op.f('ix_alphas_date_created'), table_name='alphas')
op.drop_index(op.f('ix_alphas_alpha_type'), table_name='alphas')
op.drop_table('alphas')
op.drop_table('admins')
op.drop_table('accounts')
# ### end Alembic commands ###