Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Olegsuus committed Jun 18, 2024
1 parent bc576c1 commit fc891aa
Show file tree
Hide file tree
Showing 14 changed files with 1,283 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/news-api.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"github.com/labstack/echo/v4"
"github.com/spf13/viper"
"log"
"news-api/internal/database"
"news-api/internal/handlers"
)

func main() {
// Инициализация конфигурации
viper.SetConfigName("config")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config file: %v", err)
}

// Инициализация базы данных
database.InitDB()

// Создание нового Echo инстанса
e := echo.New()

// Регистрация маршрутов
e.GET("/news", handlers.ListNewsHandler)
e.PUT("/news/:id", handlers.EditNewsHandler)

// Запуск сервера
if err := e.Start(":8080"); err != nil {
log.Fatalf("Error starting server: %v", err)
}
}
10 changes: 10 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Database:
driver: "postgres"
host: "localhost"
port: 5432
user: "olegsemashko"
password: 0000
dbname: "NewsDB"

Server:
port: 8090
2 changes: 2 additions & 0 deletions database.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
database:
dsn: "host=localhost port=5432 user=olegsemashko password=0000 dbname=NewsDB sslmode=disable"
42 changes: 42 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module news-api

go 1.22.2

require (
github.com/go-playground/validator/v10 v10.6.1
github.com/labstack/echo/v4 v4.6.3
github.com/spf13/viper v1.9.0
gopkg.in/reform.v1 v1.5.1
)

require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
749 changes: 749 additions & 0 deletions go.sum

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package config

import (
"github.com/spf13/viper"
"log"
)

type Config struct {
Database struct {
Driver string
Host string
Port string
User string
Password int
DBName string
}

Server struct {
Port int
}
}

var Cfg Config

func initConfig() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config file: %s\n", err)
}

if err := viper.Unmarshal(&Cfg); err != nil {
log.Fatalf("Unable to decode into struct, %v\n", err)
}
}
30 changes: 30 additions & 0 deletions internal/database/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package database

import (
"database/sql"
"log"

"github.com/spf13/viper"
"gopkg.in/reform.v1"
"gopkg.in/reform.v1/dialects/postgresql"
)

var DB *reform.DB

func InitDB() {
dsn := viper.GetString("database.dsn")
sqlDB, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}

DB = reform.NewDB(sqlDB, postgresql.Dialect, reform.NewPrintfLogger(log.Printf))
}

func Query(query string, args ...interface{}) (*sql.Rows, error) {
return DB.Query(query, args...)
}

func Exec(query string, args ...interface{}) (sql.Result, error) {
return DB.Exec(query, args...)
}
98 changes: 98 additions & 0 deletions internal/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package handlers

import (
"net/http"
"strconv"

"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"news-api/internal/database"
"news-api/internal/models"
)

var validate = validator.New()

type EditNewsInput struct {
Title *string `json:"Title" validate:"omitempty,min=1"`
Content *string `json:"Content" validate:"omitempty,min=1"`
Categories []int64 `json:"Categories"`
}

func EditNewsHandler(c echo.Context) error {
id := c.Param("id")

var input EditNewsInput
if err := c.Bind(&input); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid input"})
}

if err := validate.Struct(&input); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "validation failed", "details": err.Error()})
}

news, err := database.DB.FindByPrimaryKeyFrom(models.NewsTable, id)
if err != nil {
return c.JSON(http.StatusNotFound, map[string]string{"error": "news not found"})
}

if input.Title != nil {
news.(*models.News).Title = *input.Title
}
if input.Content != nil {
news.(*models.News).Content = *input.Content
}

if err := database.DB.Update(news); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update news"})
}

if input.Categories != nil {
if _, err := database.DB.Exec("DELETE FROM NewsCategories WHERE news_id = $1", id); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update categories"})
}
for _, catID := range input.Categories {
if _, err := database.DB.Exec("INSERT INTO NewsCategories (news_id, category_id) VALUES ($1, $2)", id, catID); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update categories"})
}
}
}

return c.JSON(http.StatusOK, news)
}

func ListNewsHandler(c echo.Context) error {
page, _ := strconv.Atoi(c.QueryParam("page"))
if page < 1 {
page = 1
}
limit, _ := strconv.Atoi(c.QueryParam("limit"))
if limit < 1 {
limit = 10
}
offset := (page - 1) * limit

var newsList []models.News
query := "SELECT id, title, content FROM News ORDER BY id LIMIT $1 OFFSET $2"
rows, err := database.DB.Query(query, limit, offset)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to fetch news"})
}
defer rows.Close()

for rows.Next() {
var news models.News
if err := rows.Scan(&news.ID, &news.Title, &news.Content); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to scan news"})
}
newsList = append(newsList, news)
}

var response struct {
Success bool `json:"Success"`
News []models.News `json:"News"`
}
response.Success = true
response.News = newsList

return c.JSON(http.StatusOK, response)
}
18 changes: 18 additions & 0 deletions internal/models/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package models

import _ "gopkg.in/reform.v1"

//go:generate reform

// reform:News
type News struct {
ID int64 `reform:"id,pk"`
Title string `reform:"title"`
Content string `reform:"content"`
}

// reform:NewsCategories
type NewsCategories struct {
NewsID int64 `reform:"news_id"`
CategoryID int64 `reform:"category_id"`
}
Loading

0 comments on commit fc891aa

Please sign in to comment.