Skip to content

refactor: update README files to reflect project name change from Qui… #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bd74a2b
refactor: update README files to reflect project name change from Qui…
cubxxw May 18, 2025
03b230e
feat: enhance Google OAuth login functionality and update API documen…
cubxxw May 18, 2025
31deb86
chore: streamline GitHub Actions workflow for client generation
cubxxw May 18, 2025
586141c
chore: improve GitHub Actions workflow for client generation
cubxxw May 18, 2025
01f3c8b
chore: update GitHub Actions and refactor authentication logic
cubxxw May 18, 2025
c4e60a4
chore: update OpenAPI documentation and improve type safety
cubxxw May 19, 2025
4355212
chore: update GitHub Actions workflow for client generation
cubxxw May 19, 2025
c691874
chore: update database driver handling and improve test setup
cubxxw May 19, 2025
3bb7452
chore: enhance test database setup and improve error handling
cubxxw May 19, 2025
7c19111
chore: refactor imports to use custom client types
cubxxw May 19, 2025
0ecf48c
chore: remove deprecated Google OAuth and configuration files
cubxxw May 19, 2025
4a1a8b4
chore: update import statements for ItemPublic type consistency
cubxxw May 19, 2025
546d775
chore: compress OpenAPI JSON structure for improved readability
cubxxw May 19, 2025
5ec87f5
chore: remove lint-backend workflow and add symlink for pnpm-lock.yaml
cubxxw May 19, 2025
466642e
chore: enhance .env file generation logic in Makefile
cubxxw May 19, 2025
9aefe6f
chore: enhance boolean parsing and test mode detection in pre-start s…
cubxxw May 19, 2025
9455b35
chore: update GitHub Actions workflow to install pnpm and streamline …
cubxxw May 19, 2025
1666a96
chore: update GitHub Actions workflow to install pnpm in multiple dir…
cubxxw May 19, 2025
569e60a
chore: update GitHub Actions workflows for improved dependency manage…
cubxxw May 19, 2025
9e0302e
fix: update generate-client workflow to ensure proper commit handling
cubxxw May 19, 2025
c137d78
chore: reorder Makefile targets for improved build process
cubxxw May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 15 additions & 34 deletions .github/workflows/generate-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,28 @@ name: Generate Client

on:
pull_request:
types:
- opened
- synchronize
push: # Also consider running on push to main/main to ensure consistency
branches:
- main # Or your default branch
types: [opened, synchronize]
push:
branches: [main]

permissions:
contents: write # Needed for pushing commits on same-repo events
contents: write

jobs:
generate-client:
runs-on: ubuntu-latest

steps:
# 1. Checkout Code - Single step handles both fork and same-repo PRs/pushes
- name: Checkout Code
uses: actions/checkout@v4
# Fetch depth 0 is needed for accurate diff/commit history if required elsewhere
# For pushing back, fetch the specific ref for same-repo PRs
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && github.head_ref || '' }}
# Use a PAT for same-repo events if you need to trigger other workflows
# Standard GITHUB_TOKEN is often sufficient for basic pushes
# token: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && secrets.YOUR_PAT_OR_GITHUB_TOKEN || '' }}

# 2. Setup Environment
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9.9.0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand All @@ -46,34 +37,27 @@ jobs:
with:
python-version: "3.10"

- name: Install uv (Python Package Installer)
uses: astral-sh/setup-uv@v1 # Use v1 for stable release
- name: Install uv
uses: astral-sh/setup-uv@v1
with:
# version: "0.4.15" # Specifying version is optional, can use latest stable
enable-cache: true

# 3. Install Dependencies
- name: Install Frontend Dependencies
run: pnpm install
working-directory: frontend

- name: Install Admin Dependencies
run: pnpm install
working-directory: admin

# 安装所有后端依赖项
- name: Install Backend Dependencies
run: |
uv sync
source .venv/bin/activate
pip install .
working-directory: backend

# 4. Run Generation Script with Error Handling
- name: Generate Client
# Use `uv run` to execute within the backend context if needed,
# or just run the script directly if VIRTUAL_ENV isn't strictly necessary
# for the script itself (it might just need python)
run: |
make generate-client || {
echo "❌ Failed to run generate-client.sh script."
Expand All @@ -87,17 +71,14 @@ jobs:
SENTRY_DSN: ""
POSTHOG_API_KEY: ""
POSTHOG_HOST: ""
# VIRTUAL_ENV might not be needed if uv sync --system is used
VIRTUAL_ENV: .venv

# 5. Configure Git User
- name: Configure Git User
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git config --global user.email "github-actions@github.com"
git config --global user.name "GitHub Actions"

# 6. Stage Generated Files
- name: Stage Generated Client Files
- name: Stage Generated Files
run: |
git add frontend/app/openapi-client
git add admin/src/client
Expand All @@ -110,8 +91,8 @@ jobs:
# Check if there are staged changes
if ! git diff --staged --quiet; then
echo "✅ Changes detected in generated client. Committing and pushing..."
# Pull before pushing to avoid conflicts
git pull --rebase origin ${{ github.head_ref || github.ref_name }}
# Pull before pushing to avoid conflicts if possible (optional)
# git pull --ff-only origin ${{ github.head_ref || github.ref_name }}
git commit -m "ci: ✨ Autogenerate frontend client"
git push origin HEAD:${{ github.head_ref || github.ref_name }}
else
Expand All @@ -137,4 +118,4 @@ jobs:
# DO NOT exit 1 - Allow the workflow to continue
else
echo "✅ No changes detected in generated client."
fi
fi
5 changes: 5 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9.9.0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
working-directory: frontend
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# QuickForge AI
# nexus

<div align="center">

![QuickForge AI](https://images.shields.io/badge/QuickForge-AI-blue)
![nexus](https://images.shields.io/badge/QuickForge-AI-blue)
![FastAPI](https://images.shields.io/badge/FastAPI-0.104.0-green)
![TypeScript](https://images.shields.io/badge/TypeScript-5.2.2-blue)
![License](https://images.shields.io/badge/license-MIT-brightgreen)
Expand Down Expand Up @@ -283,7 +283,7 @@ This template is optimized for use with Cursor:

### AI Integration Development

QuickForge AI makes it simple to integrate various AI services:
nexus makes it simple to integrate various AI services:

- Pre-configured connectors for popular AI APIs
- Example implementations for common AI patterns
Expand Down
15 changes: 8 additions & 7 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
</p>

<h3 align="center" style="border-bottom: none">
⭐️ QuickForge AI - 快速构建AI应用的全栈模板 ⭐️ <br>
⭐️ nexus - 快速构建AI应用的全栈模板 ⭐️ <br>
</h3>

<p align=center>
<images src="https://images.shields.io/badge/QuickForge-AI-blue" alt="QuickForge AI">
<images src="https://images.shields.io/badge/FastAPI-0.104.0-green" alt="FastAPI">
<images src="https://images.shields.io/badge/TypeScript-5.2.2-blue" alt="TypeScript">
<images src="https://images.shields.io/badge/license-MIT-brightgreen" alt="License">
<a href="https://github.com/telepace/nexus/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22"><img src="https://img.shields.io/github/issues/telepace/nexus/good%20first%20issue?logo=%22github%22" alt="good first"></a>
<a href="https://github.com/telepace/nexus"><img src="https://img.shields.io/github/stars/telepace/nexus.svg?style=flat&logo=github&colorB=deeppink&label=stars"></a>
<a href="https://discord.gg/qCyeGqaE3d"><img src="https://img.shields.io/badge/Discord-100%2B-blueviolet?logo=discord&amp;logoColor=white"></a>
<a href="https://github.com/telepace/nexus/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
<a href="https://www.python.org/"><img src="https://img.shields.io/badge/Language-Python-blue.svg"></a>
</p>

<p align="center">
Expand All @@ -20,7 +21,7 @@

<br>

QuickForge AI是一个生产级全栈模板,结合了FastAPI (Python)和TypeScript,用于快速AI原型开发。专为自由职业者和AI创业者设计,他们需要迅速构建和部署具有现代开发实践的专业应用程序。
nexus是一个生产级全栈模板,结合了FastAPI (Python)和TypeScript,用于快速AI原型开发。专为自由职业者和AI创业者设计,他们需要迅速构建和部署具有现代开发实践的专业应用程序。

## 🚀 功能特点

Expand Down Expand Up @@ -431,7 +432,7 @@ docker-compose -f docker-compose.prod.yml up -d

### AI集成开发

QuickForge AI使集成各种AI服务变得简单
nexus使集成各种AI服务变得简单

- 预配置流行AI API的连接器
- 常见AI模式的示例实现
Expand Down
1 change: 1 addition & 0 deletions admin/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export { CancelablePromise, CancelError } from "./core/CancelablePromise"
export { OpenAPI, type OpenAPIConfig } from "./core/OpenAPI"
export * from "./sdk.gen"
export * from "./types.gen"
export * from "./types"
export * from "./utils"
14 changes: 11 additions & 3 deletions admin/src/client/sdk.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';
import type { GoogleOauthGoogleCallbackApiData, GoogleOauthGoogleCallbackApiResponse, GoogleOauthGoogleLoginResponse, GoogleOauthGoogleCallbackData, GoogleOauthGoogleCallbackResponse, HealthGetHealthApiResponse, ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginLogoutResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen';
import type { GoogleOauthGoogleCallbackApiData, GoogleOauthGoogleCallbackApiResponse, GoogleOauthGoogleLoginData, GoogleOauthGoogleLoginResponse, GoogleOauthGoogleCallbackData, GoogleOauthGoogleCallbackResponse, HealthGetHealthApiResponse, ItemsReadItemsData, ItemsReadItemsResponse, ItemsCreateItemData, ItemsCreateItemResponse, ItemsReadItemData, ItemsReadItemResponse, ItemsUpdateItemData, ItemsUpdateItemResponse, ItemsDeleteItemData, ItemsDeleteItemResponse, LoginLoginAccessTokenData, LoginLoginAccessTokenResponse, LoginTestTokenResponse, LoginLogoutResponse, LoginRecoverPasswordData, LoginRecoverPasswordResponse, LoginResetPasswordData, LoginResetPasswordResponse, LoginRecoverPasswordHtmlContentData, LoginRecoverPasswordHtmlContentResponse, PrivateCreateUserData, PrivateCreateUserResponse, UsersReadUsersData, UsersReadUsersResponse, UsersCreateUserData, UsersCreateUserResponse, UsersReadUserMeResponse, UsersDeleteUserMeResponse, UsersUpdateUserMeData, UsersUpdateUserMeResponse, UsersUpdatePasswordMeData, UsersUpdatePasswordMeResponse, UsersRegisterUserData, UsersRegisterUserResponse, UsersReadUserByIdData, UsersReadUserByIdResponse, UsersUpdateUserData, UsersUpdateUserResponse, UsersDeleteUserData, UsersDeleteUserResponse, UtilsTestEmailData, UtilsTestEmailResponse, UtilsHealthCheckResponse } from './types.gen';

export class GoogleOauthService {
/**
Expand Down Expand Up @@ -31,13 +31,21 @@ export class GoogleOauthService {
* Google Login
* Initiate Google OAuth2 authentication flow
* This endpoint redirects to Google's login page
* @param data The data for the request.
* @param data.extensionCallback
* @returns unknown Successful Response
* @throws ApiError
*/
public static googleLogin(): CancelablePromise<GoogleOauthGoogleLoginResponse> {
public static googleLogin(data: GoogleOauthGoogleLoginData = {}): CancelablePromise<GoogleOauthGoogleLoginResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/api/v1/login/google'
url: '/api/v1/login/google',
query: {
extension_callback: data.extensionCallback
},
errors: {
422: 'Validation Error'
}
});
}

Expand Down
4 changes: 4 additions & 0 deletions admin/src/client/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ export type GoogleOauthGoogleCallbackApiData = {

export type GoogleOauthGoogleCallbackApiResponse = (unknown);

export type GoogleOauthGoogleLoginData = {
extensionCallback?: string;
};

export type GoogleOauthGoogleLoginResponse = (unknown);

export type GoogleOauthGoogleCallbackData = {
Expand Down
2 changes: 2 additions & 0 deletions backend/app/api/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from fastapi import APIRouter

from .extension_auth import router as extension_auth_router

# from .github import router as github_router
from .google_oauth import router as google_oauth_router
from .items import router as items_router
from .login import router as login_router

# from .profile import router as profile_router
# from .upload import router as upload_router
from .users import router as users_router
Expand Down
25 changes: 16 additions & 9 deletions backend/app/api/routes/extension_auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from datetime import timedelta
from typing import Any

import jwt
from fastapi import APIRouter, HTTPException, Request
from jwt.exceptions import InvalidTokenError

from app import crud
from app.api.deps import SessionDep
from app.core import security
from app.core.config import settings
from app.models import Token, UserPublic
from app.models import Token, User, UserPublic

router = APIRouter(tags=["extension"])

Expand All @@ -24,18 +25,22 @@ def check_extension_auth_status(request: Request, session: SessionDep) -> Any:
auth_header = request.headers.get("Authorization")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.replace("Bearer ", "")
payload = security.decode_access_token(token)
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
user_id = payload.get("sub")
user = crud.get_user(session=session, id=user_id)
user = session.get(User, user_id)
if user and user.is_active:
return {"authenticated": True, "user": UserPublic.model_validate(user)}

# 从 cookie 获取令牌
cookie_token = request.cookies.get("accessToken")
if cookie_token:
payload = security.decode_access_token(cookie_token)
payload = jwt.decode(
cookie_token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
user_id = payload.get("sub")
user = crud.get_user(session=session, id=user_id)
user = session.get(User, user_id)
if user and user.is_active:
return {"authenticated": True, "user": UserPublic.model_validate(user)}

Expand All @@ -56,9 +61,11 @@ def get_extension_token(request: Request, session: SessionDep) -> Any:
cookie_token = request.cookies.get("accessToken")
if cookie_token:
try:
payload = security.decode_access_token(cookie_token)
payload = jwt.decode(
cookie_token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
user_id = payload.get("sub")
user = crud.get_user(session=session, id=user_id)
user = session.get(User, user_id)

if user and user.is_active:
# 为扩展创建新令牌
Expand All @@ -70,7 +77,7 @@ def get_extension_token(request: Request, session: SessionDep) -> Any:
)

return Token(access_token=token)
except Exception as e:
except InvalidTokenError as e:
raise HTTPException(status_code=401, detail=f"无效的网页会话: {str(e)}")

raise HTTPException(status_code=401, detail="未找到有效的网页会话")
Expand Down
2 changes: 1 addition & 1 deletion backend/app/api/routes/google_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def google_callback_api(
@router.get("/login/google")
async def google_login(
request: Request,
extension_callback: str = None, # 添加扩展回调链接参数
extension_callback: str | None = None, # 添加扩展回调链接参数
):
"""
Initiate Google OAuth2 authentication flow
Expand Down
5 changes: 2 additions & 3 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ def all_cors_origins(self) -> list[str]:
def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
# Check if we're running in test mode and modify database name if needed
postgres_db = self.POSTGRES_DB

# Log information about test status
if self.TESTING or self.TEST_MODE:
logger.info("Test mode detected. Using test database configuration.")
# Note: We don't modify the database name here
# That will be handled by the test_db.py utilities

# Return different connection URI based on database type
"""Returns the SQLAlchemy database URI based on the configured database type.

Expand Down Expand Up @@ -207,7 +207,6 @@ def posthog_enabled(self) -> bool:
# Google OAuth
GOOGLE_CLIENT_ID: str = ""
GOOGLE_CLIENT_SECRET: str = ""
FRONTEND_HOST: str = "http://localhost:3000"
# 后端 API URL 配置,可通过环境变量覆盖
BACKEND_API_URL: str = "http://localhost:8000"

Expand Down
1 change: 1 addition & 0 deletions backend/app/core/supabase_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def get_supabase_client() -> Any | None:

# Create and return the Supabase client
try:
# 使用 type: ignore[attr-defined] 忽略 mypy 警告,因为 supabase 库在运行时确实有 create_client 方法
client = supabase.create_client( # type: ignore[attr-defined]
url, settings.SUPABASE_API_KEY
)
Expand Down
2 changes: 1 addition & 1 deletion backend/app/tests/api/routes/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def test_update_password_me(
)
# 只验证API响应成功,不再验证密码哈希匹配
assert r.status_code == 200

# 验证新token是否可用 - 验证功能性而不是具体实现
login_data = {
"username": settings.FIRST_SUPERUSER,
Expand Down
2 changes: 1 addition & 1 deletion backend/app/tests/api/test_google_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_google_callback_api_invalid_token(client: TestClient, mock_google_respo
def test_google_callback_api_mismatched_user(client: TestClient, mock_google_response):
"""Test the /auth/google-callback endpoint with mismatched user info"""
with patch("requests.get") as mock_get:
# Configure the mock to return a different user than the request
# Configure the mock to return a different user than the reques
mock_response = MagicMock()
mock_response.raise_for_status.return_value = None

Expand Down
Loading
Loading