Compare commits

..

6 Commits

Author SHA1 Message Date
Neeraj Gupta
7f8cadc022 Merge branch 'main' into meta_files 2025-09-09 10:07:07 +05:30
Laurens Priem
901bfc945e [mob][photos] Fix copy paste mistake in claude.md (#7109)
## Description

Fix copy paste mistake in claude.md
2025-09-08 16:48:46 +05:30
laurenspriem
6c25b094be Fix copy paste mistake in claude.md 2025-09-08 16:47:21 +05:30
Neeraj Gupta
e9970cf1f3 Merge remote-tracking branch 'origin/main' into meta_files 2025-09-04 10:55:31 +05:30
Neeraj Gupta
0ec6e2e0ae refactor 2025-08-19 16:18:07 +05:30
Neeraj Gupta
3442648826 Support for creating meta only files 2025-08-19 14:56:24 +05:30
12 changed files with 143 additions and 86 deletions

View File

@@ -205,4 +205,4 @@ lib/
- Always follow existing code conventions and patterns in neighboring files
# Individual Preferences
- @~/.claude/my-project-instructions.md
- @~/.claude/ente-photos-instructions.md

View File

@@ -1,45 +0,0 @@
#!/bin/sh
# ci_post_clone.sh
# This script runs after the repository is cloned
set -e
echo "🚀 Starting post-clone setup..."
# Navigate to the Flutter project root
cd ../../ # Adjust path based on your structure
# Install Flutter
echo "📦 Installing Flutter..."
FLUTTER_VERSION="3.32.8"
git clone https://github.com/flutter/flutter.git --branch $FLUTTER_VERSION --depth 1 $HOME/flutter
export PATH="$PATH:$HOME/flutter/bin"
# Verify Flutter installation
flutter --version
flutter doctor -v
# Install Rust (required for Flutter Rust Bridge)
echo "🦀 Installing Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
export PATH="$HOME/.cargo/bin:$PATH"
# Install Flutter Rust Bridge
echo "🌉 Installing Flutter Rust Bridge..."
cargo install flutter_rust_bridge_codegen
# Generate Rust bindings
echo "⚙️ Generating Rust bindings..."
flutter_rust_bridge_codegen generate
# Get Flutter dependencies
echo "📚 Getting Flutter dependencies..."
flutter pub get
# Generate iOS podfile if needed
cd ios
pod install --repo-update
echo "✅ Post-clone setup completed!"

View File

@@ -1,25 +0,0 @@
#!/bin/sh
# ci_pre_xcodebuild.sh
# This script runs before xcodebuild
set -e
echo "🔧 Pre-build setup starting..."
# Set up environment
export PATH="$PATH:$HOME/flutter/bin"
export PATH="$HOME/.cargo/bin:$PATH"
source $HOME/.cargo/env || true
# Navigate to Flutter project root
cd ../../../
# Build Flutter iOS framework
echo "🏗️ Building Flutter framework..."
flutter build ios-framework --no-debug --no-profile
# Run any code generation if needed
flutter pub run build_runner build --delete-conflicting-outputs || true
echo "✅ Pre-build setup completed!"

View File

@@ -1,10 +0,0 @@
{
"configurations": [
{
"name": "Release Build",
"scheme": "Runner",
"archive_configuration": "Release",
"destination": "generic/platform=iOS"
}
]
}

View File

@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :type => 'AGPL-3.0' }
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native

View File

@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :type => 'AGPL-3.0' }
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native

View File

@@ -480,6 +480,7 @@ func main() {
privateAPI.GET("/files/data/preview", fileHandler.GetPreviewURL)
privateAPI.POST("/files", fileHandler.CreateOrUpdate)
privateAPI.POST("/files/meta", fileHandler.CreateMetaFile)
privateAPI.POST("/files/copy", fileHandler.CopyFiles)
privateAPI.PUT("/files/update", fileHandler.Update)
privateAPI.POST("/files/trash", fileHandler.Trash)

View File

@@ -26,6 +26,20 @@ type File struct {
Info *FileInfo `json:"info,omitempty"`
}
type MetaFile struct {
ID int64 `json:"id"`
OwnerID int64 `json:"ownerID"`
CollectionID int64 `json:"collectionID"`
EncryptedKey string `json:"encryptedKey"`
KeyDecryptionNonce string `json:"keyDecryptionNonce"`
Metadata FileAttributes `json:"metadata" binding:"required"`
// IsDeleted is True when the file ID is removed from the CollectionID
IsDeleted bool `json:"isDeleted"`
UpdationTime int64 `json:"updationTime"`
MagicMetadata *MagicMetadata `json:"magicMetadata,omitempty"`
PubicMagicMetadata *MagicMetadata `json:"pubMagicMetadata,omitempty"`
}
// FileInfo has information about storage used by the file & it's metadata(future)
type FileInfo struct {
FileSize int64 `json:"fileSize,omitempty"`

View File

@@ -2,14 +2,15 @@ package api
import (
"fmt"
"github.com/ente-io/museum/pkg/controller/file_copy"
"github.com/ente-io/museum/pkg/controller/filedata"
"github.com/ente-io/museum/pkg/controller/public"
"net/http"
"os"
"strconv"
"strings"
"github.com/ente-io/museum/pkg/controller/file_copy"
"github.com/ente-io/museum/pkg/controller/filedata"
"github.com/ente-io/museum/pkg/controller/public"
"github.com/ente-io/stacktrace"
"github.com/gin-contrib/requestid"
log "github.com/sirupsen/logrus"
@@ -66,6 +67,32 @@ func (h *FileHandler) CreateOrUpdate(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
// CreateMetaFile creates an entry for a file
func (h *FileHandler) CreateMetaFile(c *gin.Context) {
userID := auth.GetUserID(c.Request.Header)
var file ente.MetaFile
if err := c.ShouldBindJSON(&file); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if file.ID != 0 {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "fileID can't be set when creating a new file"))
return
}
file.UpdationTime = time.Microseconds()
// get an ente.App from the ?app= query parameter with a default of photos
enteApp := auth.GetApp(c)
file.OwnerID = userID
file.IsDeleted = false
resp, err := h.Controller.CreateMetaFile(c, userID, file, c.Request.UserAgent(), enteApp)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, resp)
}
// CopyFiles copies files that are owned by another user
func (h *FileHandler) CopyFiles(c *gin.Context) {
var req ente.CopyFileSyncRequest

View File

@@ -0,0 +1,28 @@
package controller
import (
"github.com/ente-io/museum/ente"
"github.com/ente-io/stacktrace"
"github.com/gin-gonic/gin"
)
// CreateMetaFile adds an entry for a file in the respective tables
func (c *FileController) CreateMetaFile(ctx *gin.Context, userID int64, file ente.MetaFile, userAgent string, app ente.App) (*ente.File, error) {
collection, collErr := c.CollectionRepo.Get(file.CollectionID)
if collErr != nil {
return nil, stacktrace.Propagate(collErr, "")
}
// Verify that user owns the collection.
// Warning: Do not remove this check
if collection.Owner.ID != userID {
return nil, stacktrace.Propagate(ente.ErrPermissionDenied, "collection doesn't belong to user")
}
if collection.IsDeleted {
return nil, stacktrace.Propagate(ente.ErrCollectionDeleted, "collection has been deleted")
}
if file.OwnerID != userID {
return nil, stacktrace.Propagate(ente.ErrPermissionDenied, "file ownerID doesn't match with userID")
}
resp, err := c.FileRepo.CreateMetaFile(file, userID, app)
return resp, stacktrace.Propagate(err, "failed to create meta file")
}

View File

@@ -126,6 +126,73 @@ func (repo *FileRepository) Create(
return file, usage, stacktrace.Propagate(err, "")
}
// CreateMetaFile creates an entry in the database for the given file
func (repo *FileRepository) CreateMetaFile(
metaFile ente.MetaFile,
collectionOwnerID int64,
app ente.App,
) (*ente.File, error) {
ctx := context.Background()
tx, err := repo.DB.BeginTx(ctx, nil)
if err != nil {
return nil, stacktrace.Propagate(err, "")
}
if metaFile.OwnerID != collectionOwnerID {
return nil, stacktrace.Propagate(errors.New("both file and collection should belong to same owner"), "")
}
var fileID int64
info := &ente.FileInfo{
FileSize: 0,
ThumbnailSize: 0,
}
err = tx.QueryRowContext(ctx, `INSERT INTO files
(owner_id, encrypted_metadata,
file_decryption_header, thumbnail_decryption_header, metadata_decryption_header,
magic_metadata, pub_magic_metadata, info, updation_time)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING file_id`,
metaFile.OwnerID, metaFile.Metadata.EncryptedData, "",
"", metaFile.Metadata.DecryptionHeader,
metaFile.MagicMetadata, metaFile.PubicMagicMetadata, info,
metaFile.UpdationTime).Scan(&fileID)
if err != nil {
tx.Rollback()
return nil, stacktrace.Propagate(err, "")
}
_, err = tx.ExecContext(ctx, `INSERT INTO collection_files
(collection_id, file_id, encrypted_key, key_decryption_nonce, is_deleted, updation_time, c_owner_id, f_owner_id)
VALUES($1, $2, $3, $4, $5, $6, $7, $8)`, metaFile.CollectionID, metaFile.ID,
metaFile.EncryptedKey, metaFile.KeyDecryptionNonce, false, metaFile.UpdationTime, metaFile.OwnerID, collectionOwnerID)
if err != nil {
tx.Rollback()
return nil, stacktrace.Propagate(err, "")
}
_, err = tx.ExecContext(ctx, `UPDATE collections SET updation_time = $1
WHERE collection_id = $2`, metaFile.UpdationTime, metaFile.CollectionID)
if err != nil {
tx.Rollback()
return nil, stacktrace.Propagate(err, "")
}
err = tx.Commit()
if err != nil {
return nil, stacktrace.Propagate(err, "")
}
var file ente.File = ente.File{
ID: fileID,
UpdationTime: metaFile.UpdationTime,
OwnerID: metaFile.OwnerID,
Metadata: metaFile.Metadata,
PubicMagicMetadata: metaFile.PubicMagicMetadata,
MagicMetadata: metaFile.MagicMetadata,
EncryptedKey: metaFile.EncryptedKey,
KeyDecryptionNonce: metaFile.KeyDecryptionNonce,
Info: info,
CollectionID: collectionOwnerID,
}
return &file, stacktrace.Propagate(err, "")
}
// markAsNeedingReplication inserts new entries in object_copies, setting the
// current hot DC as the source copy.
//