2018-09-24 21:14:15 +02:00
package api
2018-09-24 11:27:46 +02:00
import (
2018-10-31 07:14:33 +01:00
"net/http"
2018-09-24 11:27:46 +02:00
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
2022-03-30 20:36:25 +02:00
2020-07-04 12:54:35 +02:00
"github.com/photoprism/photoprism/internal/acl"
2022-09-28 09:01:17 +02:00
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
2019-12-05 19:21:35 +01:00
"github.com/photoprism/photoprism/internal/form"
2022-03-30 20:36:25 +02:00
"github.com/photoprism/photoprism/internal/i18n"
2021-09-18 15:32:39 +02:00
"github.com/photoprism/photoprism/internal/search"
2022-03-30 20:36:25 +02:00
"github.com/photoprism/photoprism/internal/service"
2018-09-24 11:27:46 +02:00
)
2021-09-18 15:32:39 +02:00
// SearchPhotos searches the pictures index and returns the result as JSON.
2021-08-29 16:16:49 +02:00
//
2018-11-06 10:43:59 +01:00
// GET /api/v1/photos
2018-11-06 10:28:44 +01:00
//
2022-03-30 20:36:25 +02:00
// See form.SearchPhotos for supported search params and data types.
2021-09-18 15:32:39 +02:00
func SearchPhotos ( router * gin . RouterGroup ) {
2022-03-30 20:36:25 +02:00
// searchPhotos checking authorization and parses the search request.
2022-09-28 09:01:17 +02:00
searchForm := func ( c * gin . Context ) ( f form . SearchPhotos , s * entity . Session , err error ) {
s = AuthAny ( c , acl . ResourcePhotos , acl . Permissions { acl . ActionSearch , acl . ActionView , acl . AccessShared } )
2020-06-25 14:54:04 +02:00
2022-09-28 09:01:17 +02:00
// Abort if permission was not granted.
if s . Abort ( c ) {
return f , s , i18n . Error ( i18n . ErrForbidden )
2020-01-22 13:43:07 +01:00
}
2022-09-28 09:01:17 +02:00
// Abort if request params are invalid.
if err = c . MustBindWith ( & f , binding . Form ) ; err != nil {
event . AuditWarn ( [ ] string { ClientIP ( c ) , "session %s" , "photos" , "form invalid" , "%s" } , s . RefID , err )
2020-07-04 12:54:35 +02:00
AbortBadRequest ( c )
2022-09-28 09:01:17 +02:00
return f , s , err
2019-01-15 14:00:42 +01:00
}
2018-09-24 11:27:46 +02:00
2022-08-01 15:57:19 +02:00
// Limit results to a specific album?
if f . Album == "" {
2022-09-28 09:01:17 +02:00
if acl . Resources . Deny ( acl . ResourcePhotos , s . User ( ) . AclRole ( ) , acl . ActionSearch ) {
event . AuditErr ( [ ] string { ClientIP ( c ) , "session %s" , "%s %s as %s" , "denied" } , s . RefID , acl . ActionSearch . String ( ) , string ( acl . ResourcePhotos ) , s . User ( ) . AclRole ( ) . String ( ) )
c . AbortWithStatusJSON ( http . StatusForbidden , i18n . NewResponse ( http . StatusForbidden , i18n . ErrForbidden ) )
return f , s , i18n . Error ( i18n . ErrForbidden )
}
2022-08-01 15:57:19 +02:00
} else if a , err := entity . CachedAlbumByUID ( f . Album ) ; err != nil {
2022-09-28 09:01:17 +02:00
event . AuditWarn ( [ ] string { ClientIP ( c ) , "session %s" , "photos" , "album" , f . Album , "not found" } , s . RefID )
2022-08-01 15:57:19 +02:00
AbortAlbumNotFound ( c )
2022-09-28 09:01:17 +02:00
return f , s , i18n . Error ( i18n . ErrAlbumNotFound )
2022-08-01 15:57:19 +02:00
} else {
f . Filter = a . AlbumFilter
}
// Parse query string and filter.
if err = f . ParseQueryString ( ) ; err != nil {
log . Debugf ( "search: %s" , err )
AbortBadRequest ( c )
2022-09-28 09:01:17 +02:00
return f , s , err
2022-08-01 15:57:19 +02:00
}
conf := service . Config ( )
2022-09-28 09:01:17 +02:00
// Enforce ACL.
if acl . Resources . Deny ( acl . ResourcePhotos , s . User ( ) . AclRole ( ) , acl . AccessPrivate ) {
f . Public = true
f . Private = false
}
if acl . Resources . Deny ( acl . ResourcePhotos , s . User ( ) . AclRole ( ) , acl . ActionDelete ) {
f . Archived = false
f . Review = false
}
if acl . Resources . Deny ( acl . ResourceFiles , s . User ( ) . AclRole ( ) , acl . ActionManage ) {
f . Hidden = false
}
// Sharing link visitors may only see public content in shared albums.
if s . IsVisitor ( ) {
2020-06-26 12:16:13 +02:00
if f . Album == "" || ! s . HasShare ( f . Album ) {
2022-09-28 09:01:17 +02:00
event . AuditErr ( [ ] string { ClientIP ( c ) , "session %s" , "photos" , "shared album" , f . Album , "not shared" } , s . RefID )
AbortForbidden ( c )
return f , s , i18n . Error ( i18n . ErrUnauthorized )
2020-06-26 12:16:13 +02:00
}
2021-12-16 15:26:54 +01:00
f . UID = ""
2022-03-24 18:30:59 +01:00
f . Albums = ""
2022-08-01 15:57:19 +02:00
} else if ! conf . Settings ( ) . Features . Private {
f . Public = false
2020-06-25 14:54:04 +02:00
}
2022-09-28 09:01:17 +02:00
return f , s , nil
2022-03-30 20:36:25 +02:00
}
// defaultHandler a standard JSON result with all fields.
defaultHandler := func ( c * gin . Context ) {
2022-09-28 09:01:17 +02:00
f , s , err := searchForm ( c )
2022-03-30 20:36:25 +02:00
// Abort if authorization or form are invalid.
if err != nil {
return
}
2021-09-18 15:32:39 +02:00
result , count , err := search . Photos ( f )
2019-12-08 22:45:45 +01:00
2018-09-24 11:27:46 +02:00
if err != nil {
2022-09-28 09:01:17 +02:00
event . AuditWarn ( [ ] string { ClientIP ( c ) , "session %s" , "photos" , "search" , "%s" } , s . RefID , err )
2020-07-04 12:54:35 +02:00
AbortBadRequest ( c )
2019-01-15 14:00:42 +01:00
return
2018-09-24 11:27:46 +02:00
}
2022-03-30 20:36:25 +02:00
// Add response headers.
AddCountHeader ( c , count )
AddLimitHeader ( c , f . Count )
AddOffsetHeader ( c , f . Offset )
AddTokenHeaders ( c )
// Render as JSON.
c . JSON ( http . StatusOK , result )
}
// viewHandler returns a photo viewer formatted result.
viewHandler := func ( c * gin . Context ) {
2022-09-28 09:01:17 +02:00
f , s , err := searchForm ( c )
2022-03-30 20:36:25 +02:00
// Abort if authorization or form are invalid.
if err != nil {
return
}
conf := service . Config ( )
2022-08-01 15:57:19 +02:00
2022-03-30 20:36:25 +02:00
result , count , err := search . PhotosViewerResults ( f , conf . ContentUri ( ) , conf . ApiUri ( ) , conf . PreviewToken ( ) , conf . DownloadToken ( ) )
if err != nil {
2022-09-28 09:01:17 +02:00
event . AuditWarn ( [ ] string { ClientIP ( c ) , "session %s" , "photos" , "view" , "%s" } , s . RefID , err )
2022-03-30 20:36:25 +02:00
AbortBadRequest ( c )
return
}
// Add response headers.
2021-01-08 09:02:30 +01:00
AddCountHeader ( c , count )
AddLimitHeader ( c , f . Count )
AddOffsetHeader ( c , f . Offset )
AddTokenHeaders ( c )
2019-05-16 08:41:16 +02:00
2022-03-30 20:36:25 +02:00
// Render as JSON.
2018-09-24 11:27:46 +02:00
c . JSON ( http . StatusOK , result )
2022-03-30 20:36:25 +02:00
}
// Register route handlers.
router . GET ( "/photos" , defaultHandler )
router . GET ( "/photos/view" , viewHandler )
2018-09-27 08:59:53 +02:00
}