Compare commits
6 Commits
ios_build
...
meta_files
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f8cadc022 | ||
|
|
901bfc945e | ||
|
|
6c25b094be | ||
|
|
e9970cf1f3 | ||
|
|
0ec6e2e0ae | ||
|
|
3442648826 |
@@ -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
|
||||
|
||||
@@ -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!"
|
||||
@@ -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!"
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Release Build",
|
||||
"scheme": "Runner",
|
||||
"archive_configuration": "Release",
|
||||
"destination": "generic/platform=iOS"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
28
server/pkg/controller/file_meta.go
Normal file
28
server/pkg/controller/file_meta.go
Normal 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")
|
||||
}
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user