Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions 351004/Kuchko/ServerApp/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
2 changes: 2 additions & 0 deletions 351004/Kuchko/ServerApp/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.sh text eol=lf
*.cmd text eol=crlf
71 changes: 71 additions & 0 deletions 351004/Kuchko/ServerApp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
## =====================
## Build results
## =====================
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
artifacts/

## =====================
## .NET Core
## =====================
project.lock.json
project.fragment.lock.json

## =====================
## ASP.NET Scaffolding
## =====================
ScaffoldingReadMe.txt

## =====================
## NuGet Packages
## =====================
*.nupkg
*.snupkg
*.symbols.nupkg

## =====================
## Environment / secrets
## =====================
# dotenv
.env

# Local appsettings with secrets (Cloudinary keys, DB passwords)
appsettings.Development.json
secrets.json

# User Secrets folder (optional, depending on setup)
%APPDATA%\Microsoft\UserSecrets/
~/.microsoft/usersecrets/

## =====================
## Others
## =====================
~$*
*~
CodeCoverage/

## =====================
## Logs and test results
## =====================
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.VisualState.xml
TestResult.xml
nunit-*.xml

## =====================
## MSBuild Binary and Structured Log
## =====================
*.binlog
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using DiscussionApp.Repositories;
using Microsoft.AspNetCore.Mvc;
using SharedModels;

namespace DiscussionApp.Controllers;

[ApiController]
[Route("messages")]
public class DiscussionController(MessageRepository repo) : ControllerBase
{
[HttpPost]
public IActionResult Create([FromBody] MessageRequestTo request)
{
var newId = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var msg = new MessageResponseTo(newId, request.ArticleId, request.Content, MessageState.Approve);
repo.Create(msg);
return Ok(msg);
}

[HttpGet]
public IActionResult GetAll()
{
return Ok(repo.GetAll());
}

[HttpGet("{id:long}")]
public IActionResult GetById(long id)
{
var msg = repo.GetById(id);
return msg == null ? NotFound() : Ok(msg);
}

[HttpPut("{id:long}")]
public IActionResult Update(long id, [FromBody] MessageRequestTo request)
{
var msg = new MessageResponseTo(id, request.ArticleId, request.Content, MessageState.Approve);
repo.Update(msg);
return Ok(msg);
}

[HttpDelete("{id:long}")]
public IActionResult Delete(long id)
{
var existing = repo.GetById(id);
if (existing == null) return NotFound();
repo.Delete(id, existing.ArticleId);
return NoContent();
}
}
26 changes: 26 additions & 0 deletions 351004/Kuchko/ServerApp/DiscussionApp/DiscussionApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CassandraCSharpDriver" Version="3.22.0"/>
<PackageReference Include="Confluent.Kafka" Version="2.14.0"/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.14"/>
</ItemGroup>

<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SharedModels\SharedModels.csproj"/>
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions 351004/Kuchko/ServerApp/DiscussionApp/DiscussionApp.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@DiscussionApp_HostAddress = http://localhost:5206

GET {{DiscussionApp_HostAddress}}/weatherforecast/
Accept: application/json

###
33 changes: 33 additions & 0 deletions 351004/Kuchko/ServerApp/DiscussionApp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 1. Базовый образ
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
# ИСПРАВЛЕНО: порт по ТЗ
EXPOSE 24130
ENV ASPNETCORE_URLS=http://+:24130
ENV ASPNETCORE_ENVIRONMENT=Development

# 2. Сборка
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

# ВАЖНО: Сначала копируем проекты
COPY ["SharedModels/SharedModels.csproj", "SharedModels/"]
# ИСПРАВЛЕНО: Правильные пути с учетом контекста "." (корень решения)
COPY ["DiscussionApp/DiscussionApp.csproj", "DiscussionApp/"]
RUN dotnet restore "DiscussionApp/DiscussionApp.csproj"

COPY . .
# Переходим в папку проекта перед сборкой
WORKDIR "/src/DiscussionApp"
RUN dotnet build "DiscussionApp.csproj" -c $BUILD_CONFIGURATION -o /app/build

# 3. Публикация
FROM build AS publish
RUN dotnet publish "DiscussionApp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

# 4. Финальный запуск
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DiscussionApp.dll"]
48 changes: 48 additions & 0 deletions 351004/Kuchko/ServerApp/DiscussionApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using DiscussionApp.Repositories;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<MessageRepository>();

builder.Services.AddControllers(options =>
{
// Обязательное требование ТЗ: префикс /api/v1.0/

options.Conventions.Add(new ApiPrefixConvention(new RouteAttribute("api/v1.0")));
});

// --- 3. НАСТРОЙКА СВОБОДНОГО CORS (Для локального тестирования) ---
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy => { policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); });
});

builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment()) app.MapOpenApi();

app.UseCors();
app.UseAuthorization();
app.MapControllers();

app.Run();

public class ApiPrefixConvention(IRouteTemplateProvider route) : IApplicationModelConvention
{
private readonly AttributeRouteModel _routePrefix = new(route);

public void Apply(ApplicationModel application)
{
foreach (var selector in application.Controllers.SelectMany(c => c.Selectors))
if (selector.AttributeRouteModel != null)
selector.AttributeRouteModel =
AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel);
else
selector.AttributeRouteModel = _routePrefix;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5206",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7293;http://localhost:5206",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Cassandra;
using SharedModels;
using ISession = Cassandra.ISession;

namespace DiscussionApp.Repositories;

public class MessageRepository
{
private readonly ISession _session;

public MessageRepository(IConfiguration config)
{
var contactPoint = config["Cassandra:ContactPoint"] ?? "localhost";
var cluster = Cluster.Builder().AddContactPoint(contactPoint).Build();
_session = cluster.Connect();
InitializeDatabase();
}

private void InitializeDatabase()
{
_session.Execute(
"CREATE KEYSPACE IF NOT EXISTS distcomp WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};");
_session.ChangeKeyspace("distcomp");
_session.Execute(@"CREATE TABLE IF NOT EXISTS tbl_message (
article_id bigint, id bigint, content text, state text, country text,
PRIMARY KEY ((article_id), id));");
}

public IEnumerable<MessageResponseTo> GetAll()
{
var rows = _session.Execute("SELECT * FROM tbl_message ALLOW FILTERING");
return rows.Select(MapRow);
}

public MessageResponseTo? GetById(long id)
{
var ps = _session.Prepare("SELECT * FROM tbl_message WHERE id = ? ALLOW FILTERING");
var row = _session.Execute(ps.Bind(id)).FirstOrDefault();
return row != null ? MapRow(row) : null;
}

public void Create(MessageResponseTo msg)
{
var ps = _session.Prepare(
"INSERT INTO tbl_message (article_id, id, content, state, country) VALUES (?, ?, ?, ?, ?)");
_session.Execute(ps.Bind(msg.ArticleId, msg.Id, msg.Content, msg.State, "Unknown"));
}

public void Update(MessageResponseTo msg)
{
var ps = _session.Prepare("UPDATE tbl_message SET content = ?, state = ? WHERE article_id = ? AND id = ?");
_session.Execute(ps.Bind(msg.Content, msg.State, msg.ArticleId, msg.Id));
}

public void Delete(long id, long articleId)
{
var ps = _session.Prepare("DELETE FROM tbl_message WHERE article_id = ? AND id = ?");
_session.Execute(ps.Bind(articleId, id));
}

private MessageResponseTo MapRow(Row r)
{
return new MessageResponseTo(
r.GetValue<long>("id"), r.GetValue<long>("article_id"),
r.GetValue<string>("content"), r.GetValue<string>("state"));
}
}
Loading