Мне очень хотелось иметь в архитектуре упорядоченность, модульность и DI от NestJs, при этом для frontend очень нужны функции NextJs, а еще очень хотелось иметь typesafe потоки данных. При этом для современного frontend просто необходимы SSR, SSG, Router (NextJs), и конечно же typesafe коммуникация между ними. В этом starter репозитории я связал эти фреймворки через trpc, импортировал общий роутер во frontend и добавил декораторы, для более нативного вида в NestJS.
🆕 Добавлена авторизация через Google OAuth с TRPC методами!
# 1. Клонируйте репозиторий
git clone https://github.com/isaev-the-poetry/neSXt
cd neSXt
# Установка зависимостей для backend
cd backend
npm install
# Установка зависимостей для frontend
cd ../frontend
npm install# В backend/.env
DATABASE_URL="file:./dev.db"
JWT_SECRET="your-super-secret-jwt-key-change-this-in-production"
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
FRONTEND_URL="http://localhost:3000"
BACKEND_URL="http://localhost:4000"cd backend
npx prisma generate
npx prisma migrate dev --name "init"# Терминал 1: Backend
cd backend
npm run start:dev
# Терминал 2: Frontend
cd frontend
npm run dev- Frontend: http://localhost:3000
- Backend API: http://localhost:4000
- Тест аутентификации: http://localhost:3000/test-auth
- ✅ Google OAuth - Полностью настроен
- 🚧 GitHub OAuth - Готов к настройке
- 🚧 Discord OAuth - Готов к настройке
- 🚧 Facebook OAuth - Готов к настройке
- 🚧 Twitter OAuth - Готов к настройке
- 🚧 Apple OAuth - Готов к настройке
// Получить активные токены пользователя
const { data: tokens } = trpc.auth.getUserTokens.useQuery({ activeOnly: true });
// Отозвать все токены (выйти со всех устройств)
await revokeAllTokensMutation.mutateAsync();
// Очистить просроченные токены
await cleanupTokensMutation.mutateAsync();// TRPC endpoint с аутентификацией
@Query()
@Output(UserSchema)
async getCurrentUser(@AuthUser() user: User) {
return user;
}
// Frontend использование
const { user, isAuthenticated, logout } = useAuth();- Пользователь нажимает "Войти через Google"
- Перенаправление на Google OAuth
- Google callback → валидация → создание токена
- Сохранение в базе данных с метаданными
- Frontend получает токен и данные пользователя
- Автоматическая валидация при каждом запросе
Универсальная схема поддерживает:
- users - Основные данные пользователей
- auth_providers - Конфигурация провайдеров OAuth
- user_providers - Связка пользователей с провайдерами
- user_tokens - JWT токены с метаданными
- user_sessions - Управление сессиями (опционально)
@Injectable()
export class ExampleController {
@Query()
@Output(z.object({ message: z.string() }))
async hello() {
return { message: 'Hello from TRPC!' };
}
@Mutation()
@Input(z.object({ name: z.string() }))
@Output(z.object({ greeting: z.string() }))
async greet(@Input() input: { name: string }) {
return { greeting: `Hello, ${input.name}!` };
}
@Query()
@Output(UserSchema)
async getProfile(@AuthUser() user: User) {
return user;
}
}// Query
const { data: hello } = trpc.example.hello.useQuery();
// Mutation
const greetMutation = trpc.example.greet.useMutation();
const handleGreet = () => {
greetMutation.mutate({ name: 'World' });
};
// Аутентифицированный запрос
const { data: profile } = trpc.example.getProfile.useQuery();- JWT токены в базе данных
- Мгновенный отзыв токенов
- IP и User-Agent отслеживание
- Автоматическое истечение токенов
- Детальное логирование событий
// Автоматическая валидация для всех запросов
export class AuthMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (token) {
const validation = await this.authService.validateToken(token);
if (validation.isValid) {
req.auth = { user: validation.user, token: validation.token };
}
}
next();
}
}- UNIVERSAL_AUTH_SYSTEM.md - Полная документация по системе аутентификации
- ROLES_SYSTEM.md - Система ролей и контроль доступа
- GOOGLE_OAUTH_SETUP.md - Настройка Google OAuth
- QUICK_OAUTH_ENABLE.md - Быстрая активация OAuth
// Основные TRPC endpoints
auth.getGoogleAuthUrl() // Получить URL для OAuth
auth.getAvailableProviders() // Список провайдеров
auth.getCurrentUser() // Текущий пользователь
auth.getUserProfile() // Детальный профиль
auth.getUserTokens() // Токены пользователя
auth.signOut() // Выход из системы
auth.revokeToken() // Отозвать токен
auth.revokeAllTokens() // Отозвать все токены
auth.cleanupExpiredTokens() // Очистить просроченные- Создать стратегию Passport.js
- Добавить REST endpoints
- Обновить типы и конфигурацию
- Протестировать OAuth flow
// Создание нового декоратора
export const CustomDecorator = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.customData;
},
);
// Использование
@Query()
async method(@CustomDecorator() data: any) {
return data;
}# Создание миграции
npx prisma migrate dev --name "add_new_feature"
# Генерация клиента
npx prisma generate
# Просмотр данных
npx prisma studiofunction Dashboard() {
const { user, isAuthenticated, isLoading } = useAuth();
if (isLoading) return <div>Загрузка...</div>;
if (!isAuthenticated) return <LoginPage />;
return (
<div>
<h1>Привет, {user.name}!</h1>
<UserProfile user={user} />
</div>
);
}@Query()
@Output(z.array(PostSchema))
async getUserPosts(@AuthUser() user: User) {
return this.postsService.getPostsByUser(user.id);
}function TokenManager() {
const { data: tokens } = trpc.auth.getUserTokens.useQuery();
const revokeAllMutation = trpc.auth.revokeAllTokens.useMutation();
return (
<div>
<h2>Активные сессии: {tokens?.tokens.length}</h2>
{tokens?.tokens.map(token => (
<TokenInfo key={token.id} token={token} />
))}
<button onClick={() => revokeAllMutation.mutate()}>
Выйти со всех устройств
</button>
</div>
);
}# Переменные окружения для production
NODE_ENV=production
DATABASE_URL="postgresql://user:pass@host:5432/db"
JWT_SECRET="super-secure-secret-key"
FRONTEND_URL="https://yourdomain.com"
BACKEND_URL="https://api.yourdomain.com"# Dockerfile для backend
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npx prisma generate
EXPOSE 4000
CMD ["npm", "run", "start:prod"]LOGIN_SUCCESS/LOGIN_FAILEDTOKEN_CREATED/TOKEN_REVOKEDPROVIDER_LINKED/PROVIDER_UNLINKEDROLE_ASSIGNED/ROLE_REMOVED
- Активные пользователи
- Популярность провайдеров
- Среднее время сессии
- Распределение по ролям
// Только для администраторов
@Mutation()
@Auth()
@HasRole(Role.ADMIN)
async assignUserRole(input: { userId: string; role: string }) {
return this.authService.assignRole(input.userId, input.role);
}
// Для менеджеров и выше
@Query()
@Auth()
@HasRole(Role.MANAGER)
async getAllUsers() {
return this.authService.getAllUsers();
}
// Множественные роли (OR логика)
@Query()
@Auth()
@HasRole([Role.MANAGER, Role.ADMIN])
async getReports() {
return this.reportsService.generate();
}function AdminPanel() {
const { user, hasRole } = useAuth();
if (!hasRole('ADMIN')) {
return <div>Access denied</div>;
}
return (
<div>
<h1>Admin Panel</h1>
<UserRoleManager />
<SystemSettings />
</div>
);
}- Fork проекта
- Создайте feature branch (
git checkout -b feature/AmazingFeature) - Commit изменения (
git commit -m 'Add some AmazingFeature') - Push в branch (
git push origin feature/AmazingFeature) - Откройте Pull Request
Этот проект распространяется под лицензией MIT. См. LICENSE для деталей.
- 🔐 Полнофункциональная система аутентификации
- 🌐 Google OAuth интеграция
- 💾 Управление токенами в БД
- 👥 Система ролей с контролем доступа
- 🎨 Custom TRPC декораторы
- 📱 Responsive frontend
- 🛡️ Middleware безопасности
- 📊 Детальное логирование
NeSXt - современное решение для быстрой разработки full-stack приложений с продвинутой аутентификацией и контролем доступа! 🚀