Skip to content
Snippets Groups Projects
Commit 066686cf authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement all basic APIs

parent 780ad861
No related branches found
No related tags found
1 merge request!1Replace entire project structure
Pipeline #2582 failed
Showing
with 235 additions and 73 deletions
package api
import (
"encoding/json"
"git.kuschku.de/justjanne/imghost-frontend/auth"
"git.kuschku.de/justjanne/imghost-frontend/environment"
"git.kuschku.de/justjanne/imghost-frontend/model"
"git.kuschku.de/justjanne/imghost-frontend/util"
"net/http"
)
func CreateAlbum(env environment.FrontendEnvironment) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
var err error
user, err := auth.ParseUser(request, env)
if err != nil {
http.Error(writer, err.Error(), http.StatusUnauthorized)
return
}
println("parsed user: " + user.Name)
var data model.Album
err = json.NewDecoder(request.Body).Decode(&data)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
data.Id = generateId()
data.Owner = user.Id
err = env.Repositories.Albums.Create(data)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
album, err := env.Repositories.Albums.Get(data.Id)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
util.ReturnJson(writer, album)
})
}
......@@ -30,6 +30,11 @@ func GetAlbum(env environment.FrontendEnvironment) http.Handler {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
image.Metadata, err = env.Repositories.ImageMetadata.List(image)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
album.Images[i] = image
}
......
......@@ -34,6 +34,11 @@ func ListAlbums(env environment.FrontendEnvironment) http.Handler {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
image.Metadata, err = env.Repositories.ImageMetadata.List(image)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
album.Images[j] = image
}
albums[i] = album
......
......@@ -23,7 +23,7 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler {
return
}
var changes []model.AlbumImage
var changes []model.Image
err = json.NewDecoder(request.Body).Decode(&changes)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
......@@ -32,8 +32,8 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler {
for index, image := range changes {
image.Album = album.Id
err = env.Repositories.AlbumImages.Reorder(image, index)
position := index
err = env.Repositories.AlbumImages.Reorder(image, position)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
......
package api
import (
"encoding/json"
"git.kuschku.de/justjanne/imghost-frontend/environment"
"git.kuschku.de/justjanne/imghost-frontend/model"
"github.com/gorilla/mux"
"net/http"
)
func CreateAlbumImage(env environment.FrontendEnvironment) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
var data model.Image
err := json.NewDecoder(request.Body).Decode(&data)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
data.Album = vars["albumId"]
err = env.Repositories.AlbumImages.Create(data)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
writer.WriteHeader(http.StatusNoContent)
})
}
......@@ -25,7 +25,7 @@ func UpdateAlbumImage(env environment.FrontendEnvironment) http.Handler {
return
}
var changes model.AlbumImage
var changes model.Image
err = json.NewDecoder(request.Body).Decode(&changes)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
......
......@@ -51,6 +51,9 @@ func main() {
router.Handle(
"/api/v1/albums",
api.ListAlbums(env)).Methods(http.MethodGet, http.MethodOptions)
router.Handle(
"/api/v1/albums",
api.CreateAlbum(env)).Methods(http.MethodPost, http.MethodOptions)
router.Handle(
"/api/v1/albums/{albumId}",
api.GetAlbum(env)).Methods(http.MethodGet, http.MethodOptions)
......@@ -68,6 +71,9 @@ func main() {
router.Handle(
"/api/v1/albums/{albumId}/images/{imageId}",
api.UpdateAlbumImage(env)).Methods(http.MethodPost, http.MethodOptions)
router.Handle(
"/api/v1/albums/{albumId}/images",
api.CreateAlbumImage(env)).Methods(http.MethodPost, http.MethodOptions)
router.Handle(
"/api/v1/albums/{albumId}/images/{imageId}",
api.DeleteAlbumImage(env)).Methods(http.MethodDelete, http.MethodOptions)
......
......@@ -6,13 +6,13 @@ import (
)
type Album struct {
Id string `json:"id" db:"id"`
Owner string `json:"owner" db:"owner"`
Title string `json:"title" db:"title"`
Description string `json:"description" db:"description"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
Images []AlbumImage `json:"images"`
Id string `json:"id" db:"id"`
Owner string `json:"owner" db:"owner"`
Title string `json:"title" db:"title"`
Description string `json:"description" db:"description"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
Images []Image `json:"images"`
}
func (album Album) VerifyOwner(user User) error {
......
package model
import (
"git.kuschku.de/justjanne/imghost-frontend/configuration"
"git.kuschku.de/justjanne/imghost-frontend/storage"
)
type AlbumImage struct {
Album string `json:"album" db:"album"`
Image string `json:"image" db:"image"`
Title string `json:"title" db:"title"`
Description string `json:"description" db:"description"`
Url string `json:"url"`
}
func (image *AlbumImage) LoadUrl(storage storage.Storage, config configuration.StorageConfiguration) (err error) {
image.Url = storage.UrlFor(config.ImageBucket, image.Image).String()
return
}
......@@ -8,6 +8,7 @@ import (
)
type Image struct {
Album string `json:"album,omitempty" db:"album"`
Id string `json:"id" db:"id"`
Owner string `json:"owner" db:"owner"`
Title string `json:"title" db:"title"`
......
......@@ -19,26 +19,42 @@ type AlbumImages struct {
func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
repo.db = db
repo.queryList, err = db.PrepareNamed(`
SELECT album,
image,
title,
description
SELECT album_images.album,
images.id,
albums.owner,
album_images.title,
album_images.description,
albums.created_at,
albums.updated_at,
images.original_name,
images.type,
images.state
FROM album_images
WHERE album = :albumId
ORDER BY position
JOIN albums ON album_images.album = albums.id
JOIN images ON album_images.image = images.id
WHERE album_images.album = :albumId
ORDER BY album_images.position
`)
if err != nil {
return
}
repo.queryGet, err = db.PrepareNamed(`
SELECT album,
image,
title,
description
SELECT album_images.album,
images.id,
albums.owner,
album_images.title,
album_images.description,
albums.created_at,
albums.updated_at,
images.original_name,
images.type,
images.state
FROM album_images
WHERE album = :albumId
AND image = :imageId
ORDER BY position
JOIN albums ON album_images.album = albums.id
JOIN images ON album_images.image = images.id
WHERE album_images.album = :albumId
AND album_images.image = :imageId
ORDER BY album_images.position
`)
if err != nil {
return
......@@ -67,7 +83,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
repo.stmtDelete, err = db.PrepareNamed(`
DELETE FROM album_images
WHERE album = :albumId
AND image = :imageID
AND image = :imageId
`)
if err != nil {
return
......@@ -92,7 +108,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
return repo, nil
}
func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err error) {
func (repo AlbumImages) List(albumId string) (images []model.Image, err error) {
rows, err := repo.queryList.Queryx(map[string]interface{}{
"albumId": albumId,
})
......@@ -100,7 +116,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err
return
}
for rows.Next() {
var image model.AlbumImage
var image model.Image
err = rows.StructScan(&image)
if err != nil {
return
......@@ -110,7 +126,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err
return
}
func (repo AlbumImages) Get(albumId string, imageId string) (image model.AlbumImage, err error) {
func (repo AlbumImages) Get(albumId string, imageId string) (image model.Image, err error) {
err = repo.queryGet.Get(&image, map[string]interface{}{
"albumId": albumId,
"imageId": imageId,
......@@ -118,46 +134,46 @@ func (repo AlbumImages) Get(albumId string, imageId string) (image model.AlbumIm
return
}
func (repo AlbumImages) Create(new model.AlbumImage) (err error) {
func (repo AlbumImages) Create(new model.Image) (err error) {
_, err = repo.stmtCreate.Exec(map[string]interface{}{
"albumId": new.Album,
"imageId": new.Image,
"imageId": new.Id,
"title": new.Title,
"description": new.Description,
})
return
}
func (repo AlbumImages) Update(changed model.AlbumImage) (err error) {
func (repo AlbumImages) Update(changed model.Image) (err error) {
_, err = repo.stmtUpdate.Exec(map[string]interface{}{
"albumId": changed.Album,
"imageId": changed.Image,
"imageId": changed.Id,
"title": changed.Title,
"description": changed.Description,
})
return
}
func (repo AlbumImages) Reorder(changed model.AlbumImage, position int) (err error) {
func (repo AlbumImages) Reorder(changed model.Image, position int) (err error) {
_, err = repo.stmtReorder.Exec(map[string]interface{}{
"albumId": changed.Album,
"imageId": changed.Image,
"imageId": changed.Id,
"position": position,
})
return
}
func (repo AlbumImages) Delete(changed model.AlbumImage) (err error) {
func (repo AlbumImages) Delete(changed model.Image) (err error) {
_, err = repo.stmtDelete.Exec(map[string]interface{}{
"albumId": changed.Album,
"imageId": changed.Image,
"imageId": changed.Id,
})
return
}
func (repo AlbumImages) DeleteAll(changed model.AlbumImage) (err error) {
func (repo AlbumImages) DeleteAll(changed model.Album) (err error) {
_, err = repo.stmtDeleteAll.Exec(map[string]interface{}{
"albumId": changed.Album,
"albumId": changed.Id,
})
return
}
import './App.css';
import {BaseUrlProvider} from './api/baseUrlContext';
import {BaseUrlProvider} from './api/util/BaseUrlContext';
import {QueryClient, QueryClientProvider} from "react-query";
import {BrowserRouter, Link, Route} from "react-router-dom";
import {Redirect, Switch} from "react-router";
import {AppBar, Button, Toolbar, Typography} from "@material-ui/core";
import {useErrorDisplay} from "./components/ErrorContext";
import {useErrorDisplay} from "./components/error/ErrorContext";
import {AlbumListPage} from "./pages/AlbumListPage";
import {ImageDetailPage} from './pages/ImageDetailPage';
import {ImageListPage} from "./pages/ImageListPage";
import {UploadView} from "./components/UploadView";
import {AlbumDetailPage} from "./pages/AlbumDetailPage";
const queryClient = new QueryClient();
......@@ -62,6 +63,9 @@ export function App() {
<Route path="/albums">
<AlbumListPage/>
</Route>
<Route path="/a/:albumId">
<AlbumDetailPage/>
</Route>
<Redirect from="/" to="/images"/>
</Switch>
</ErrorWrapper>
......
export interface AlbumCreate {
title: string,
description: string
}
export interface AlbumImage {
album: string,
image: string,
title: string,
description: string,
url: string,
import {Image} from "./Image";
export interface AlbumImage extends Image {
album: string
}
export interface AlbumImageCreate {
album: string,
id: string,
title: string,
description: string
}
import {ImageState} from "./ImageState";
export interface Image {
id: string,
owner: string,
......@@ -13,11 +15,3 @@ export interface Image {
},
url: string,
}
export enum ImageState {
CREATED = "created",
QUEUED = "queued",
IN_PROGRESS = "in_progress",
DONE = "done",
ERROR = "error"
}
export enum ImageState {
CREATED = "created",
QUEUED = "queued",
IN_PROGRESS = "in_progress",
DONE = "done",
ERROR = "error"
}
import {useBaseUrl} from "./util/BaseUrlContext";
import {useMutation, useQueryClient} from "react-query";
import axios from "axios";
import {Album} from "./model/Album";
import {AlbumCreate} from "./model/AlbumCreate";
export const useCreateAlbum = () => {
const baseUrl = useBaseUrl();
const queryClient = useQueryClient();
return useMutation<Album, unknown, AlbumCreate>((album: AlbumCreate) => axios.post(
`api/v1/albums`,
album,
{
baseURL: baseUrl
}
), {
onSuccess: () => {
queryClient.invalidateQueries('album')
},
})
}
import {useBaseUrl} from "./util/BaseUrlContext";
import {useMutation, useQueryClient} from "react-query";
import axios from "axios";
import {AlbumImageCreate} from "./model/AlbumImageCreate";
export const useCreateAlbumImage = () => {
const baseUrl = useBaseUrl();
const queryClient = useQueryClient();
return useMutation<void, unknown, AlbumImageCreate>((albumImage: AlbumImageCreate) => axios.post(
`api/v1/albums/${albumImage.album}/images`,
albumImage,
{
baseURL: baseUrl
}
), {
onSuccess: () => {
queryClient.invalidateQueries('album')
},
})
}
import {useBaseUrl} from "./util/BaseUrlContext";
import {useMutation, useQueryClient} from "react-query";
import axios from "axios";
import {Album} from "./model/Album";
export const useDeleteAlbum = () => {
const baseUrl = useBaseUrl();
const queryClient = useQueryClient();
return useMutation<void, unknown, Album>((album: Album) => axios.delete(
`api/v1/albums/${album.id}`,
{
baseURL: baseUrl
}
), {
onSuccess: () => {
queryClient.invalidateQueries('album')
},
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment