Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9b053e4
feat: загрузка фронтенд части проекта CodeFlow
amirjons Jan 28, 2026
35f4f48
ci: GitHub Actions CI
Akim112 Jan 29, 2026
eb9ab0c
chore: add .env to .gitignore
Akim112 Jan 29, 2026
a33a373
fix: eslint config and lint errors for CI
Akim112 Jan 29, 2026
2c94544
docs: add pull request template
Akim112 Jan 29, 2026
c0bfca8
Очистка репозитория для новой структуры
amirjons Feb 1, 2026
ebe4dda
Новая структура репозитория (без workflow файлов)
amirjons Feb 1, 2026
30b892e
Добавлены workflow файлы CI/CD
amirjons Feb 1, 2026
208e936
Новая структура репозитория. Frontend проект добавлен в docs/frontend
amirjons Feb 1, 2026
e8c130e
Создана структура репозитория. Frontend проект в docs/frontend
amirjons Feb 1, 2026
3b2c23a
chore: restore CI workflow
Akim112 Feb 1, 2026
4b2cc84
fix: add ESLint deps, flat config, fix all lint errors for CI
Akim112 Feb 1, 2026
6d223fb
fix: add missing PostCSS dependencies for build
Akim112 Feb 1, 2026
3a341be
docs: add PR template
Akim112 Feb 1, 2026
def63a3
chore: restructure project layout via terminal
amirjons Feb 4, 2026
cf32ccc
feat: upload frontend project files
amirjons Feb 4, 2026
253d4ee
chore: add CI
Akim112 Feb 4, 2026
aa89f2a
Merge branch 'develop' of github.com:Akim112/codeflow into develop
Akim112 Feb 4, 2026
0208698
feat: add backend infrastructure with ASP.NET Core 8, PostgreSQL, JWT…
Akim112 Feb 4, 2026
3de3b2c
docs: add README
Akim112 Feb 4, 2026
5161451
fix: sync package-lock with package.json and fix permissions
amirjons Feb 4, 2026
980a68e
:wqMerge branch 'develop' of https://github.com/Akim112/codeflow into…
amirjons Feb 4, 2026
c8c6b43
fix: remove deprecated --ext flag from eslint script
amirjons Feb 4, 2026
7a02877
fix: add missing typescript-eslint dependency
amirjons Feb 4, 2026
5d097d3
chore: fix all lint errors and verify build
amirjons Feb 4, 2026
e42a1a2
Update ci-frontend.yml
amirjons Feb 4, 2026
bf846a3
fix: use legacy-peer-deps in CI and cleanup dependencies
amirjons Feb 4, 2026
63a55ec
Merge branch 'develop' of https://github.com/Akim112/codeflow into de…
amirjons Feb 4, 2026
5a25cc3
fix: add missing postcss dependencies for Mantine
amirjons Feb 4, 2026
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
Empty file added .editorconfig
Empty file.
16 changes: 16 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Описание
<!-- Кратко: что сделано в этом PR -->

## Тип изменений
- [ ] Новая фича (frontend)
- [ ] Новая фича (backend)
- [ ] Исправление бага
- [ ] Рефакторинг / документация

## Чеклист
- [ ] `npm run lint` проходит (если затронут frontend)
- [ ] `npm run build` проходит (если затронут frontend)
- [ ] Проверено локально

## Связанные задачи
<!-- Номер issue или «нет» -->
27 changes: 27 additions & 0 deletions .github/workflows/ci-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# CI для CodeFlow (backend)
name: CI Backend

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore
run: dotnet restore
working-directory: backend

- name: Build
run: dotnet build --no-restore -c Release
working-directory: backend
33 changes: 33 additions & 0 deletions .github/workflows/ci-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# CI для CodeFlow (frontend)
name: CI Frontend

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm install --legacy-peer-deps
working-directory: frontend

- name: Lint
run: npm run lint
working-directory: frontend

- name: Build
run: npm run build
working-directory: frontend
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Фронтенд
node_modules/
dist/
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Бэкенд (будет позже)
bin/obj/
*.csproj.user

# Общее
.DS_Store
Thumbs.db
.idea/
.vscode/
*.swp
*.swo
69 changes: 68 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,68 @@
# codeflow
## CodeFlow

Онлайн-сервис для изучения программирования с элементами геймификации.

## Структура репозитория

- **frontend/** — SPA (React, TypeScript, Vite)
- **backend/** — API (ASP.NET Core 8, C#)
- **.github/workflows/** — CI (frontend и backend)
- **docker-compose.yml** — PostgreSQL + API для локального запуска

## Требования

- Node.js 20+ (для frontend)
- .NET 8 SDK (для backend)
- Docker и Docker Compose (опционально, для БД и API)

## Быстрый запуск

- **1. БД и Backend**

```bash
docker-compose up -d
```

- **Или по отдельности:**
- **2. База данных (PostgreSQL)**
- Через Docker:

```bash
docker-compose up -d postgres
```



- **3. Backend (API)**

```bash
cd backend
dotnet restore
dotnet run --project CodeFlow.Api
```

API: `http://localhost:5001`, Swagger: `http://localhost:5001/swagger`.

- **3. Frontend**

```bash
cd frontend
npm install
npm run dev
```

Фронт: `http://localhost:5173`.

## Что сейчас сделано

- **Фронтенд**
- SPA на React + TypeScript + Vite.
- Экран курса и урока, редактор кода, локальное хранение прогресса.
- Дизайн и базовая геймификация (XP, уровни, достижения, фракции, магазин) — пока в основном на клиенте.

- **Бэкенд**
- ASP.NET Core 8 API, PostgreSQL (EF Core), JWT‑аутентификация.
- Курсы, уроки, прогресс, рейтинг, достижения, фракции, магазин, уведомления.
- Роли `User/Teacher/Admin`, админ‑эндпоинты для курсов, уроков и пользователей.
- Проверка Python‑кода: синхронно и асинхронно через очередь и фоновый воркер.
- Экспорт отчётов по прогрессу и рейтингу в CSV и PDF.
Empty file added backend/.gitkeep
Empty file.
22 changes: 22 additions & 0 deletions backend/CodeFlow.Api/CodeFlow.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>CodeFlow.Api</RootNamespace>
<AssemblyName>CodeFlow.Api</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="QuestPDF" Version="2024.12.2" />
</ItemGroup>

</Project>
44 changes: 44 additions & 0 deletions backend/CodeFlow.Api/Controllers/AchievementsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using CodeFlow.Api.Data;
using CodeFlow.Api.DTOs;

namespace CodeFlow.Api.Controllers;

[ApiController]
[Route("api/[controller]")]
public class AchievementsController : ControllerBase
{
private readonly AppDbContext _db;

public AchievementsController(AppDbContext db)
{
_db = db;
}

[HttpGet]
public async Task<ActionResult<IEnumerable<AchievementDefinitionDto>>> GetAll(CancellationToken ct)
{
var list = await _db.AchievementDefinitions
.Select(a => new AchievementDefinitionDto(a.Id, a.Title, a.Description, a.Icon, a.Rarity))
.ToListAsync(ct);
return Ok(list);
}

[HttpGet("me")]
[Authorize]
public async Task<ActionResult<IEnumerable<UserAchievementDto>>> GetMyAchievements(CancellationToken ct)
{
var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userIdStr == null || !Guid.TryParse(userIdStr, out var userId))
return Unauthorized();
var list = await _db.UserAchievements
.Where(a => a.UserId == userId)
.OrderBy(a => a.UnlockedAtUtc)
.Select(a => new UserAchievementDto(a.AchievementId, a.UnlockedAtUtc))
.ToListAsync(ct);
return Ok(list);
}
}
80 changes: 80 additions & 0 deletions backend/CodeFlow.Api/Controllers/Admin/AdminCoursesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using CodeFlow.Api.Data;
using CodeFlow.Api.DTOs;
using CodeFlow.Api.Models;

namespace CodeFlow.Api.Controllers.Admin;

[ApiController]
[Route("api/admin/courses")]
[Authorize(Policy = "AdminOrTeacher")]
public class AdminCoursesController : ControllerBase
{
private readonly AppDbContext _db;

public AdminCoursesController(AppDbContext db)
{
_db = db;
}

[HttpGet]
public async Task<ActionResult<IEnumerable<CourseDto>>> GetAll(CancellationToken ct)
{
var list = await _db.Courses
.Select(c => new CourseDto(c.Id, c.Title, c.Description, c.Level, c.Color, c.TotalLessons))
.ToListAsync(ct);
return Ok(list);
}

[HttpGet("{id:int}")]
public async Task<ActionResult<CourseDto>> GetById(int id, CancellationToken ct)
{
var course = await _db.Courses.FindAsync(new object[] { id }, ct);
if (course == null) return NotFound();
return Ok(new CourseDto(course.Id, course.Title, course.Description, course.Level, course.Color, course.TotalLessons));
}

[HttpPost]
public async Task<ActionResult<CourseDto>> Create([FromBody] CreateCourseRequest request, CancellationToken ct)
{
var id = await _db.Courses.AnyAsync(ct) ? await _db.Courses.MaxAsync(c => c.Id, ct) + 1 : 1;
var course = new Course
{
Id = id,
Title = request.Title,
Description = request.Description,
Level = request.Level ?? "",
Color = request.Color ?? "green",
TotalLessons = request.TotalLessons
};
_db.Courses.Add(course);
await _db.SaveChangesAsync(ct);
return CreatedAtAction(nameof(GetById), new { id = course.Id }, new CourseDto(course.Id, course.Title, course.Description, course.Level, course.Color, course.TotalLessons));
}

[HttpPut("{id:int}")]
public async Task<ActionResult<CourseDto>> Update(int id, [FromBody] UpdateCourseRequest request, CancellationToken ct)
{
var course = await _db.Courses.FindAsync(new object[] { id }, ct);
if (course == null) return NotFound();
if (request.Title != null) course.Title = request.Title;
if (request.Description != null) course.Description = request.Description;
if (request.Level != null) course.Level = request.Level;
if (request.Color != null) course.Color = request.Color;
if (request.TotalLessons.HasValue) course.TotalLessons = request.TotalLessons.Value;
await _db.SaveChangesAsync(ct);
return Ok(new CourseDto(course.Id, course.Title, course.Description, course.Level, course.Color, course.TotalLessons));
}

[HttpDelete("{id:int}")]
public async Task<ActionResult> Delete(int id, CancellationToken ct)
{
var course = await _db.Courses.FindAsync(new object[] { id }, ct);
if (course == null) return NotFound();
_db.Courses.Remove(course);
await _db.SaveChangesAsync(ct);
return NoContent();
}
}
Loading