Serve compiled front from the same application

also add embedded build for production
This commit is contained in:
2026-02-11 01:12:53 +01:00
parent 8fc5ff84c2
commit e127088195
28 changed files with 200 additions and 130 deletions

5
front/frontend.go Normal file
View File

@@ -0,0 +1,5 @@
package front
import "io/fs"
var Frontend fs.FS

11
front/frontend_dev.go Normal file
View File

@@ -0,0 +1,11 @@
//go:build !embed
package front
import (
"os"
)
func init() {
Frontend = os.DirFS("./dist")
}

19
front/frontend_embed.go Normal file
View File

@@ -0,0 +1,19 @@
//go:build embed
package front
import (
"embed"
"io/fs"
)
//go:embed dist
var frontend embed.FS
func init() {
var err error
Frontend, err = fs.Sub(frontend, "dist")
if err != nil {
panic(err)
}
}

View File

@@ -7,7 +7,7 @@
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "vite --host",
"dev": "vite build --watch",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --fix",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,13 +1,11 @@
import { useAuthStore } from './auth.store.js'
const baseUrl = "http://localhost:8080"
export function getInventaireImagePathOrDefault(path) {
return getImagePathOrGivenDefault(path, "../../defaultinventairebook.png")
return getImagePathOrGivenDefault(path, "../../image/defaultinventairebook.png")
}
export function getImagePathOrDefault(path) {
return getImagePathOrGivenDefault(path, "../defaultbook.png")
return getImagePathOrGivenDefault(path, "../image/defaultbook.png")
}
export function getImagePathOrGivenDefault(path, defaultpath) {
@@ -16,7 +14,7 @@ export function getImagePathOrGivenDefault(path, defaultpath) {
} else if (path.startsWith("https://")) {
return path;
} else {
return baseUrl + path;
return path;
}
}
@@ -38,78 +36,78 @@ function useFetch(data, error, url) {
export function getMyBooks(data, error, arg, limit, offset) {
const queryParams = new URLSearchParams({limit: limit, offset: offset});
return useFetch(data, error, baseUrl + '/mybooks/' + arg + "?" + queryParams.toString());
return useFetch(data, error, '/ws/mybooks/' + arg + "?" + queryParams.toString());
}
export function getSearchBooks(data, error, searchterm, lang, searchInventaire, limit, offset) {
const queryParams = new URLSearchParams({lang: lang, inventaire: searchInventaire, limit: limit, offset: offset});
return useFetch(data, error, baseUrl + '/search/' + encodeURIComponent(searchterm) + "?" + queryParams.toString());
return useFetch(data, error, '/ws/search/' + encodeURIComponent(searchterm) + "?" + queryParams.toString());
}
export function getInventaireEditionBooks(data, error, inventaireId, lang, limit, offset) {
const queryParams = new URLSearchParams({lang: lang, limit: limit, offset: offset});
return useFetch(data, error, baseUrl + '/inventaire/books/' + encodeURIComponent(inventaireId) + "?" + queryParams.toString());
return useFetch(data, error, '/ws/inventaire/books/' + encodeURIComponent(inventaireId) + "?" + queryParams.toString());
}
export function getAuthor(data, error, id) {
return useFetch(data, error, baseUrl + '/author/' + id);
return useFetch(data, error, '/ws/author/' + id);
}
export function getAuthorBooks(data, error, id, limit, offset) {
const queryParams = new URLSearchParams({limit: limit, offset: offset});
return useFetch(data, error, baseUrl + '/author/' + id + "/books" + "?" + queryParams.toString());
return useFetch(data, error, '/ws/author/' + id + "/books" + "?" + queryParams.toString());
}
export function getBook(data, error, id) {
return useFetch(data, error, baseUrl + '/book/' + id);
return useFetch(data, error, '/ws/book/' + id);
}
export function postBook(book) {
return genericPayloadCall('/book', book.value, 'POST')
return genericPayloadCall('/ws/book', book.value, 'POST')
}
export async function postImportBook(id, language) {
return genericPayloadCall('/importbook', {inventaireid: id, lang: language}, 'POST');
return genericPayloadCall('/ws/importbook', {inventaireid: id, lang: language}, 'POST');
}
export async function putReadBook(bookId) {
return genericPayloadCall('/book/' + bookId + "/read", {read: true}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/read", {read: true}, 'PUT')
}
export async function putUnreadBook(bookId) {
return genericPayloadCall('/book/' + bookId + "/read", {read: false}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/read", {read: false}, 'PUT')
}
export async function putEndReadDate(bookId, enddate) {
return genericPayloadCall('/book/' + bookId + "/read", {read: true, endDate: enddate}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/read", {read: true, endDate: enddate}, 'PUT')
}
export async function putEndReadDateUnset(bookId) {
return genericPayloadCall('/book/' + bookId + "/read", {read: true, endDate: "null"}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/read", {read: true, endDate: "null"}, 'PUT')
}
export async function putStartReadDateUnset(bookId) {
return genericPayloadCall('/book/' + bookId + "/startread", {startDate: "null"}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/startread", {startDate: "null"}, 'PUT')
}
export async function putStartReadDate(bookId, startdate) {
return genericPayloadCall('/book/' + bookId + "/startread", {startDate: startdate}, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/startread", {startDate: startdate}, 'PUT')
}
export async function putWantReadBook(bookId, payload) {
return genericPayloadCall('/book/' + bookId + "/wantread", payload, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/wantread", payload, 'PUT')
}
export async function putRateBook(bookId, payload) {
return genericPayloadCall('/book/' + bookId + "/rate", payload, 'PUT')
return genericPayloadCall('/ws/book/' + bookId + "/rate", payload, 'PUT')
}
export function postLogin(user) {
return genericPostCallNoAuth('/auth/login', user.value)
return genericPostCallNoAuth('/ws/auth/login', user.value)
}
export function postSignUp(user) {
return genericPostCallNoAuth('/auth/signup', user.value)
return genericPostCallNoAuth('/ws/auth/signup', user.value)
}
export function postImage(file) {
@@ -117,7 +115,7 @@ export function postImage(file) {
const formData = new FormData();
formData.append('file', file);
if (user != null) {
return fetch(baseUrl + "/upload/cover", {
return fetch("/ws/upload/cover", {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + user.token
@@ -130,7 +128,7 @@ export function postImage(file) {
}
export function genericPostCallNoAuth(apiRoute, object) {
return fetch(baseUrl + apiRoute, {
return fetch(apiRoute, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -143,7 +141,7 @@ export function genericPayloadCall(apiRoute, object, method) {
const { user } = useAuthStore();
if (user != null) {
return fetch(baseUrl + apiRoute, {
return fetch(apiRoute, {
method: method,
headers: {
'Content-Type': 'application/json',

31
go.mod
View File

@@ -5,30 +5,30 @@ go 1.25.1
require (
github.com/alecthomas/kong v1.14.0
github.com/alecthomas/kong-toml v0.4.0
github.com/gin-contrib/cors v1.7.6
github.com/gin-gonic/gin v1.10.1
github.com/go-playground/validator/v10 v10.27.0
github.com/gin-gonic/gin v1.11.0
github.com/go-playground/validator/v10 v10.30.1
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/nicksnyder/go-i18n/v2 v2.6.0
github.com/pelletier/go-toml v1.9.5
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.42.0
golang.org/x/text v0.30.0
github.com/stretchr/testify v1.11.1
golang.org/x/crypto v0.48.0
golang.org/x/text v0.34.0
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.0
)
require (
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.1 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -41,11 +41,14 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
golang.org/x/arch v0.21.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sys v0.36.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect
golang.org/x/arch v0.24.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sys v0.41.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

72
go.sum
View File

@@ -10,34 +10,34 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -53,8 +53,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@@ -76,34 +76,42 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -36,7 +36,7 @@ func testGetAuthor(t *testing.T, authorId string, status int) fetchedAuthor {
router := testutils.TestSetup()
token := testutils.ConnectDemoUser(router)
req, _ := http.NewRequest("GET", "/author/"+authorId, nil)
req, _ := http.NewRequest("GET", "/ws/author/"+authorId, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

View File

@@ -47,7 +47,7 @@ func TestSearchBookPerAuthor_Offset(t *testing.T) {
func testFetchBookAuthor(t *testing.T, authorId uint, limit string, offset string) bookAuthorGetResult {
router := testutils.TestSetup()
u, err := url.Parse(fmt.Sprintf("/author/%d/books", authorId))
u, err := url.Parse(fmt.Sprintf("/ws/author/%d/books", authorId))
if err != nil {
t.Error(err)
}

View File

@@ -21,7 +21,7 @@ func TestGetBook_Ok(t *testing.T) {
AuthorID: 2,
Rating: 10,
Read: true,
CoverPath: "/bookcover/dunchateaulautre.jpg",
CoverPath: "/static/bookcover/dunchateaulautre.jpg",
}, book)
}
@@ -33,7 +33,7 @@ func TestGetBook_NoUserBook(t *testing.T) {
Author: "Truman Capote",
AuthorID: 14,
Read: false,
CoverPath: "/bookcover/desangfroid.jpg",
CoverPath: "/static/bookcover/desangfroid.jpg",
}, book)
}
@@ -48,7 +48,7 @@ func TestGetBook_Description(t *testing.T) {
Summary: "Lorsque le célèbre aviateur Charles Lindbergh battit le président Roosevelt aux élections présidentielles de 1940, la peur s'empara des Juifs américains. Non seulement Lindbergh avait, dans son discours radiophonique à la nation, reproché aux Juifs de pousser l'Amérique à entreprendre une guerre inutile avec l'Allemagne nazie, mais, en devenant trente-troisième président des États-Unis, il s'empressa de signer un pacte de non-agression avec Hitler. Alors la terreur pénétra dans les foyers juifs, notamment dans celui de la famille Roth. Ce contexte sert de décor historique au Complot contre l'Amérique, un roman où Philip Roth, qui avait sept ans à l'époque, raconte ce que vécut et ressentit sa famille - et des millions de familles semblables dans tout le pays - lors des lourdes années où s'exerça la présidence de Lindbergh, quand les citoyens américains qui étaient aussi des Juifs avaient de bonnes raisons de craindre le pire. Ce faisant, il nous offre un nouveau chef-d'oeuvre.",
Rating: 6,
Read: true,
CoverPath: "/bookcover/lecomplotcontrelamerique.jpg",
CoverPath: "/static/bookcover/lecomplotcontrelamerique.jpg",
}, book)
}
@@ -64,7 +64,7 @@ func testGetBook(t *testing.T, id string, status int) dto.BookGet {
router := testutils.TestSetup()
token := testutils.ConnectDemoUser(router)
req, _ := http.NewRequest("GET", "/book/"+id, nil)
req, _ := http.NewRequest("GET", "/ws/book/"+id, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

View File

@@ -73,12 +73,12 @@ func TestGetReadBooksHandler_CheckOneBook(t *testing.T) {
Author: "Truman Capote",
Rating: 6,
Read: true,
CoverPath: "/bookcover/desangfroid.jpg",
CoverPath: "/static/bookcover/desangfroid.jpg",
}, book)
}
func testGetReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookUserGet {
u, err := url.Parse("/mybooks/read")
u, err := url.Parse("/ws/mybooks/read")
if err != nil {
t.Error(err)
}

View File

@@ -29,7 +29,7 @@ func TestGetReadingBooksHandler_Demo2(t *testing.T) {
}
func testGetReadingBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookUserGet {
u, err := url.Parse("/mybooks/reading")
u, err := url.Parse("/ws/mybooks/reading")
if err != nil {
t.Error(err)
}

View File

@@ -28,5 +28,5 @@ func TestGetWantReadBooksHandler_Demo2(t *testing.T) {
}
func testGetWantReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int) dto.BookUserGet {
return testGetbooksHandler(t, router, userToken, expectedCode, "/mybooks/wantread")
return testGetbooksHandler(t, router, userToken, expectedCode, "/ws/mybooks/wantread")
}

View File

@@ -68,7 +68,7 @@ func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) {
w := httptest.NewRecorder()
token := testutils.ConnectDemoUser(router)
req, _ := http.NewRequest("POST", "/book",
req, _ := http.NewRequest("POST", "/ws/book",
strings.NewReader(string(bookJson)))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
router.ServeHTTP(w, req)

View File

@@ -23,7 +23,7 @@ func TestPostImportBookHandler_Ok(t *testing.T) {
assert.Equal(t, "les Hauts de Hurle-Vent", book.Title)
assert.Equal(t, "Emily Brontë", book.Author)
assert.Equal(t, "isbn:9782253004752", book.InventaireId)
assert.Equal(t, "/bookcover/44abbcbdc1092212c2bae66f5165019dac1e2a7b.webp", book.CoverPath)
assert.Equal(t, "/static/bookcover/44abbcbdc1092212c2bae66f5165019dac1e2a7b.webp", book.CoverPath)
}
func TestPostImportBookHandler_OkAuthorKey(t *testing.T) {
@@ -32,7 +32,7 @@ func TestPostImportBookHandler_OkAuthorKey(t *testing.T) {
assert.Equal(t, "Dr Bloodmoney", book.Title)
assert.Equal(t, "Philip K. Dick", book.Author)
assert.Equal(t, "isbn:9782290033630", book.InventaireId)
assert.Equal(t, "/bookcover/1d1493159d031224a42b37c4417fcbb8c76b00bd.webp", book.CoverPath)
assert.Equal(t, "/static/bookcover/1d1493159d031224a42b37c4417fcbb8c76b00bd.webp", book.CoverPath)
}
func TestPostImportBookHandler_NoOLID(t *testing.T) {
@@ -49,7 +49,7 @@ func testPostImportBookHandler(t *testing.T, openlibraryid string, expectedCode
"lang":"fr"
}`
queryJson = fmt.Sprintf(queryJson, openlibraryid)
req, _ := http.NewRequest("POST", "/importbook",
req, _ := http.NewRequest("POST", "/ws/importbook",
strings.NewReader(string(queryJson)))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
router.ServeHTTP(w, req)

View File

@@ -59,7 +59,7 @@ func testPostUserHandler(t *testing.T, userJson string, expectedCode int) {
router := testutils.TestSetup()
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/auth/signup",
req, _ := http.NewRequest("POST", "/ws/auth/signup",
strings.NewReader(string(userJson)))
router.ServeHTTP(w, req)

View File

@@ -83,5 +83,5 @@ func TestPutRatingUserBooksHandler_BadBookId(t *testing.T) {
}
func testPutRateUserBooks(t *testing.T, payload string, bookId string, expectedCode int) {
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/book/"+bookId+"/rate")
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/ws/book/"+bookId+"/rate")
}

View File

@@ -59,5 +59,5 @@ func TestPutReadUserBooks_UnsetReadOk(t *testing.T) {
}
func testPutReadUserBooks(t *testing.T, payload string, bookId string, expectedCode int) {
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/book/"+bookId+"/read")
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/ws/book/"+bookId+"/read")
}

View File

@@ -49,5 +49,5 @@ func TestPutStartReadUserBooks_Unset(t *testing.T) {
}
func testPutStartReadUserBooks(t *testing.T, payload string, bookId string, expectedCode int) {
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/book/"+bookId+"/startread")
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/ws/book/"+bookId+"/startread")
}

View File

@@ -31,5 +31,5 @@ func TestPutWantRead_SetFalse(t *testing.T) {
}
func testPutWantReadUserBooks(t *testing.T, payload string, bookId string, expectedCode int) {
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/book/"+bookId+"/wantread")
testutils.TestBookPutCallWithDemoPayload(t, payload, bookId, expectedCode, "/ws/book/"+bookId+"/wantread")
}

View File

@@ -32,7 +32,7 @@ func TestSearchBook_OneBookNotUserBook(t *testing.T) {
Rating: 0,
Read: false,
WantRead: false,
CoverPath: "/bookcover/iliade.jpeg",
CoverPath: "/static/bookcover/iliade.jpeg",
}},
result.Books)
}
@@ -48,7 +48,7 @@ func TestSearchBook_OneBookRead(t *testing.T) {
Rating: 7,
Read: true,
WantRead: false,
CoverPath: "/bookcover/lesdieuxontsoif.jpg",
CoverPath: "/static/bookcover/lesdieuxontsoif.jpg",
}},
result.Books)
}
@@ -64,7 +64,7 @@ func TestSearchBook_ISBN(t *testing.T) {
Rating: 6,
Read: true,
WantRead: false,
CoverPath: "/bookcover/lecomplotcontrelamerique.jpg",
CoverPath: "/static/bookcover/lecomplotcontrelamerique.jpg",
}},
result.Books)
}
@@ -102,7 +102,7 @@ func TestSearchBook_Offset(t *testing.T) {
func testSearchBook(t *testing.T, searchterm string, limit string, offset string) dto.BookSearchGet {
router := testutils.TestSetup()
u, err := url.Parse("/search/" + searchterm)
u, err := url.Parse("/ws/search/" + searchterm)
if err != nil {
t.Error(err)
}

View File

@@ -94,5 +94,5 @@ func computePathFromName(ac *appcontext.AppContext, filename string) string {
}
func GetWsLinkPrefix() string {
return "/bookcover/"
return "/static/bookcover/"
}

View File

@@ -13,12 +13,12 @@ func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
//do not check current user if we are creating an account or logging in
if strings.HasPrefix(c.FullPath(), "/auth/") {
if strings.HasPrefix(c.FullPath(), "/ws/auth/") {
return
}
//do not check static files
if strings.HasPrefix(c.FullPath(), "/bookcover/") {
if strings.HasPrefix(c.FullPath(), "/static/bookcover/") {
return
}

View File

@@ -1,9 +1,14 @@
package setup
import (
"github.com/gin-contrib/cors"
"bufio"
"io"
"io/fs"
"net/http"
"github.com/gin-gonic/gin"
"git.artlef.fr/PersonalLibraryManager/front"
"git.artlef.fr/PersonalLibraryManager/internal/appcontext"
"git.artlef.fr/PersonalLibraryManager/internal/config"
"git.artlef.fr/PersonalLibraryManager/internal/db"
@@ -20,69 +25,90 @@ func Setup(config *config.Config) *gin.Engine {
panic(err)
}
r := gin.Default()
r.Use(cors.New(configureCors())) // All origins allowed by default
r.Use(middleware.Auth())
r.Static("/bookcover", config.ImageFolderPath)
bundle := i18nresource.InitializeI18n()
r.GET("/mybooks/read", func(c *gin.Context) {
ws := r.Group("/ws")
ws.Use(middleware.Auth())
ws.GET("/mybooks/read", func(c *gin.Context) {
routes.GetMyBooksReadHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/mybooks/reading", func(c *gin.Context) {
ws.GET("/mybooks/reading", func(c *gin.Context) {
routes.GetMyBooksReadingHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/mybooks/wantread", func(c *gin.Context) {
ws.GET("/mybooks/wantread", func(c *gin.Context) {
routes.GetMyBooksWantReadHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/search/:searchterm", func(c *gin.Context) {
ws.GET("/search/:searchterm", func(c *gin.Context) {
routes.GetSearchBooksHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/inventaire/books/:workId", func(c *gin.Context) {
ws.GET("/inventaire/books/:workId", func(c *gin.Context) {
routes.GetInventaireBooks(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/book/:id", func(c *gin.Context) {
ws.GET("/book/:id", func(c *gin.Context) {
routes.GetBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.PUT("/book/:id/read", func(c *gin.Context) {
ws.PUT("/book/:id/read", func(c *gin.Context) {
routes.PutReadUserBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.PUT("/book/:id/wantread", func(c *gin.Context) {
ws.PUT("/book/:id/wantread", func(c *gin.Context) {
routes.PutWantReadUserBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.PUT("/book/:id/startread", func(c *gin.Context) {
ws.PUT("/book/:id/startread", func(c *gin.Context) {
routes.PutStartReadUserBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.PUT("/book/:id/rate", func(c *gin.Context) {
ws.PUT("/book/:id/rate", func(c *gin.Context) {
routes.PutRateUserBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/book", func(c *gin.Context) {
ws.POST("/book", func(c *gin.Context) {
routes.PostBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/importbook", func(c *gin.Context) {
ws.POST("/importbook", func(c *gin.Context) {
routes.PostImportBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/author/:id", func(c *gin.Context) {
ws.GET("/author/:id", func(c *gin.Context) {
routes.GetAuthorHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.GET("/author/:id/books", func(c *gin.Context) {
ws.GET("/author/:id/books", func(c *gin.Context) {
routes.GetAuthorBooksHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/auth/signup", func(c *gin.Context) {
ws.POST("/auth/signup", func(c *gin.Context) {
routes.PostSignupHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/auth/login", func(c *gin.Context) {
ws.POST("/auth/login", func(c *gin.Context) {
routes.PostLoginHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.POST("/upload/cover", func(c *gin.Context) {
ws.POST("/upload/cover", func(c *gin.Context) {
routes.PostUploadBookCoverHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
})
r.Static("/static/bookcover", config.ImageFolderPath)
folders := []string{"assets", "css", "image"}
for _, folder := range folders {
subFs, err := fs.Sub(front.Frontend, folder)
if err != nil {
panic(err)
}
r.StaticFS("/"+folder, http.FS(subFs))
}
r.StaticFileFS("/favicon.ico", "favicon.ico", http.FS(front.Frontend))
r.GET("/", func(c *gin.Context) {
indexHtml, err := front.Frontend.Open("index.html")
if err != nil {
panic(err)
}
defer indexHtml.Close()
c.Header("Content-Type", "text/html")
fileReader := bufio.NewReader(indexHtml)
_, err = io.Copy(c.Writer, fileReader)
})
r.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusFound, "/")
})
return r
}
func configureCors() cors.Config {
config := cors.DefaultConfig()
config.AllowOrigins = []string{"http://localhost:5173"}
config.AllowPrivateNetwork = true
config.AllowCredentials = true
config.AllowHeaders = []string{"Authorization", "Content-Type"}
return config
}

View File

@@ -45,7 +45,7 @@ func ConnectDemo2User(router *gin.Engine) string {
func connectUser(router *gin.Engine, loginJson string) string {
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/auth/login", strings.NewReader(loginJson))
req, _ := http.NewRequest("POST", "/ws/auth/login", strings.NewReader(loginJson))
router.ServeHTTP(w, req)
var parsedResponse loginResponse
err := json.Unmarshal(w.Body.Bytes(), &parsedResponse)