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 { ...@@ -30,6 +30,11 @@ func GetAlbum(env environment.FrontendEnvironment) http.Handler {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return return
} }
image.Metadata, err = env.Repositories.ImageMetadata.List(image)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
album.Images[i] = image album.Images[i] = image
} }
......
...@@ -34,6 +34,11 @@ func ListAlbums(env environment.FrontendEnvironment) http.Handler { ...@@ -34,6 +34,11 @@ func ListAlbums(env environment.FrontendEnvironment) http.Handler {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return return
} }
image.Metadata, err = env.Repositories.ImageMetadata.List(image)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
album.Images[j] = image album.Images[j] = image
} }
albums[i] = album albums[i] = album
......
...@@ -23,7 +23,7 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler { ...@@ -23,7 +23,7 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler {
return return
} }
var changes []model.AlbumImage var changes []model.Image
err = json.NewDecoder(request.Body).Decode(&changes) err = json.NewDecoder(request.Body).Decode(&changes)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
...@@ -32,8 +32,8 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler { ...@@ -32,8 +32,8 @@ func ReorderAlbum(env environment.FrontendEnvironment) http.Handler {
for index, image := range changes { for index, image := range changes {
image.Album = album.Id image.Album = album.Id
position := index
err = env.Repositories.AlbumImages.Reorder(image, index) err = env.Repositories.AlbumImages.Reorder(image, position)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 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 { ...@@ -25,7 +25,7 @@ func UpdateAlbumImage(env environment.FrontendEnvironment) http.Handler {
return return
} }
var changes model.AlbumImage var changes model.Image
err = json.NewDecoder(request.Body).Decode(&changes) err = json.NewDecoder(request.Body).Decode(&changes)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
......
...@@ -51,6 +51,9 @@ func main() { ...@@ -51,6 +51,9 @@ func main() {
router.Handle( router.Handle(
"/api/v1/albums", "/api/v1/albums",
api.ListAlbums(env)).Methods(http.MethodGet, http.MethodOptions) api.ListAlbums(env)).Methods(http.MethodGet, http.MethodOptions)
router.Handle(
"/api/v1/albums",
api.CreateAlbum(env)).Methods(http.MethodPost, http.MethodOptions)
router.Handle( router.Handle(
"/api/v1/albums/{albumId}", "/api/v1/albums/{albumId}",
api.GetAlbum(env)).Methods(http.MethodGet, http.MethodOptions) api.GetAlbum(env)).Methods(http.MethodGet, http.MethodOptions)
...@@ -68,6 +71,9 @@ func main() { ...@@ -68,6 +71,9 @@ func main() {
router.Handle( router.Handle(
"/api/v1/albums/{albumId}/images/{imageId}", "/api/v1/albums/{albumId}/images/{imageId}",
api.UpdateAlbumImage(env)).Methods(http.MethodPost, http.MethodOptions) 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( router.Handle(
"/api/v1/albums/{albumId}/images/{imageId}", "/api/v1/albums/{albumId}/images/{imageId}",
api.DeleteAlbumImage(env)).Methods(http.MethodDelete, http.MethodOptions) api.DeleteAlbumImage(env)).Methods(http.MethodDelete, http.MethodOptions)
......
...@@ -12,7 +12,7 @@ type Album struct { ...@@ -12,7 +12,7 @@ type Album struct {
Description string `json:"description" db:"description"` Description string `json:"description" db:"description"`
CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
Images []AlbumImage `json:"images"` Images []Image `json:"images"`
} }
func (album Album) VerifyOwner(user User) error { 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 ( ...@@ -8,6 +8,7 @@ import (
) )
type Image struct { type Image struct {
Album string `json:"album,omitempty" db:"album"`
Id string `json:"id" db:"id"` Id string `json:"id" db:"id"`
Owner string `json:"owner" db:"owner"` Owner string `json:"owner" db:"owner"`
Title string `json:"title" db:"title"` Title string `json:"title" db:"title"`
......
...@@ -19,26 +19,42 @@ type AlbumImages struct { ...@@ -19,26 +19,42 @@ type AlbumImages struct {
func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) { func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
repo.db = db repo.db = db
repo.queryList, err = db.PrepareNamed(` repo.queryList, err = db.PrepareNamed(`
SELECT album, SELECT album_images.album,
image, images.id,
title, albums.owner,
description album_images.title,
album_images.description,
albums.created_at,
albums.updated_at,
images.original_name,
images.type,
images.state
FROM album_images FROM album_images
WHERE album = :albumId JOIN albums ON album_images.album = albums.id
ORDER BY position JOIN images ON album_images.image = images.id
WHERE album_images.album = :albumId
ORDER BY album_images.position
`) `)
if err != nil { if err != nil {
return return
} }
repo.queryGet, err = db.PrepareNamed(` repo.queryGet, err = db.PrepareNamed(`
SELECT album, SELECT album_images.album,
image, images.id,
title, albums.owner,
description album_images.title,
album_images.description,
albums.created_at,
albums.updated_at,
images.original_name,
images.type,
images.state
FROM album_images FROM album_images
WHERE album = :albumId JOIN albums ON album_images.album = albums.id
AND image = :imageId JOIN images ON album_images.image = images.id
ORDER BY position WHERE album_images.album = :albumId
AND album_images.image = :imageId
ORDER BY album_images.position
`) `)
if err != nil { if err != nil {
return return
...@@ -67,7 +83,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) { ...@@ -67,7 +83,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
repo.stmtDelete, err = db.PrepareNamed(` repo.stmtDelete, err = db.PrepareNamed(`
DELETE FROM album_images DELETE FROM album_images
WHERE album = :albumId WHERE album = :albumId
AND image = :imageID AND image = :imageId
`) `)
if err != nil { if err != nil {
return return
...@@ -92,7 +108,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) { ...@@ -92,7 +108,7 @@ func NewAlbumImageRepo(db *sqlx.DB) (repo AlbumImages, err error) {
return repo, nil 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{}{ rows, err := repo.queryList.Queryx(map[string]interface{}{
"albumId": albumId, "albumId": albumId,
}) })
...@@ -100,7 +116,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err ...@@ -100,7 +116,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err
return return
} }
for rows.Next() { for rows.Next() {
var image model.AlbumImage var image model.Image
err = rows.StructScan(&image) err = rows.StructScan(&image)
if err != nil { if err != nil {
return return
...@@ -110,7 +126,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err ...@@ -110,7 +126,7 @@ func (repo AlbumImages) List(albumId string) (images []model.AlbumImage, err err
return 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{}{ err = repo.queryGet.Get(&image, map[string]interface{}{
"albumId": albumId, "albumId": albumId,
"imageId": imageId, "imageId": imageId,
...@@ -118,46 +134,46 @@ func (repo AlbumImages) Get(albumId string, imageId string) (image model.AlbumIm ...@@ -118,46 +134,46 @@ func (repo AlbumImages) Get(albumId string, imageId string) (image model.AlbumIm
return 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{}{ _, err = repo.stmtCreate.Exec(map[string]interface{}{
"albumId": new.Album, "albumId": new.Album,
"imageId": new.Image, "imageId": new.Id,
"title": new.Title, "title": new.Title,
"description": new.Description, "description": new.Description,
}) })
return 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{}{ _, err = repo.stmtUpdate.Exec(map[string]interface{}{
"albumId": changed.Album, "albumId": changed.Album,
"imageId": changed.Image, "imageId": changed.Id,
"title": changed.Title, "title": changed.Title,
"description": changed.Description, "description": changed.Description,
}) })
return 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{}{ _, err = repo.stmtReorder.Exec(map[string]interface{}{
"albumId": changed.Album, "albumId": changed.Album,
"imageId": changed.Image, "imageId": changed.Id,
"position": position, "position": position,
}) })
return 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{}{ _, err = repo.stmtDelete.Exec(map[string]interface{}{
"albumId": changed.Album, "albumId": changed.Album,
"imageId": changed.Image, "imageId": changed.Id,
}) })
return 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{}{ _, err = repo.stmtDeleteAll.Exec(map[string]interface{}{
"albumId": changed.Album, "albumId": changed.Id,
}) })
return return
} }
import './App.css'; import './App.css';
import {BaseUrlProvider} from './api/baseUrlContext'; import {BaseUrlProvider} from './api/util/BaseUrlContext';
import {QueryClient, QueryClientProvider} from "react-query"; import {QueryClient, QueryClientProvider} from "react-query";
import {BrowserRouter, Link, Route} from "react-router-dom"; import {BrowserRouter, Link, Route} from "react-router-dom";
import {Redirect, Switch} from "react-router"; import {Redirect, Switch} from "react-router";
import {AppBar, Button, Toolbar, Typography} from "@material-ui/core"; 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 {AlbumListPage} from "./pages/AlbumListPage";
import {ImageDetailPage} from './pages/ImageDetailPage'; import {ImageDetailPage} from './pages/ImageDetailPage';
import {ImageListPage} from "./pages/ImageListPage"; import {ImageListPage} from "./pages/ImageListPage";
import {UploadView} from "./components/UploadView"; import {UploadView} from "./components/UploadView";
import {AlbumDetailPage} from "./pages/AlbumDetailPage";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
...@@ -62,6 +63,9 @@ export function App() { ...@@ -62,6 +63,9 @@ export function App() {
<Route path="/albums"> <Route path="/albums">
<AlbumListPage/> <AlbumListPage/>
</Route> </Route>
<Route path="/a/:albumId">
<AlbumDetailPage/>
</Route>
<Redirect from="/" to="/images"/> <Redirect from="/" to="/images"/>
</Switch> </Switch>
</ErrorWrapper> </ErrorWrapper>
......
export interface AlbumCreate {
title: string,
description: string
}
export interface AlbumImage { import {Image} from "./Image";
album: string,
image: string, export interface AlbumImage extends Image {
title: string, album: string
description: string,
url: string,
} }
export interface AlbumImageCreate {
album: string,
id: string,
title: string,
description: string
}
import {ImageState} from "./ImageState";
export interface Image { export interface Image {
id: string, id: string,
owner: string, owner: string,
...@@ -13,11 +15,3 @@ export interface Image { ...@@ -13,11 +15,3 @@ export interface Image {
}, },
url: string, 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.
Please register or to comment