Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,138 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/create.go
|
||||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
|
||||
)
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) Create(ctx context.Context, usage *storagedailyusage.StorageDailyUsage) error {
|
||||
if usage == nil {
|
||||
return fmt.Errorf("storage daily usage cannot be nil")
|
||||
}
|
||||
|
||||
// Ensure usage day is truncated to date only
|
||||
usage.UsageDay = usage.UsageDay.Truncate(24 * time.Hour)
|
||||
|
||||
query := `INSERT INTO storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
(user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
|
||||
err := impl.Session.Query(query,
|
||||
usage.UserID,
|
||||
usage.UsageDay,
|
||||
usage.TotalBytes,
|
||||
usage.TotalAddBytes,
|
||||
usage.TotalRemoveBytes,
|
||||
).WithContext(ctx).Exec()
|
||||
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to create storage daily usage",
|
||||
zap.String("user_id", usage.UserID.String()),
|
||||
zap.Time("usage_day", usage.UsageDay),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to create storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) CreateMany(ctx context.Context, usages []*storagedailyusage.StorageDailyUsage) error {
|
||||
if len(usages) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := impl.Session.NewBatch(gocql.LoggedBatch).WithContext(ctx)
|
||||
|
||||
for _, usage := range usages {
|
||||
if usage == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure usage day is truncated to date only
|
||||
usage.UsageDay = usage.UsageDay.Truncate(24 * time.Hour)
|
||||
|
||||
batch.Query(`INSERT INTO storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
(user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes)
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
usage.UserID,
|
||||
usage.UsageDay,
|
||||
usage.TotalBytes,
|
||||
usage.TotalAddBytes,
|
||||
usage.TotalRemoveBytes,
|
||||
)
|
||||
}
|
||||
|
||||
err := impl.Session.ExecuteBatch(batch)
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to create multiple storage daily usages", zap.Error(err))
|
||||
return fmt.Errorf("failed to create multiple storage daily usages: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) IncrementUsage(ctx context.Context, userID gocql.UUID, usageDay time.Time, totalBytes, addBytes, removeBytes int64) error {
|
||||
// Ensure usage day is truncated to date only
|
||||
usageDay = usageDay.Truncate(24 * time.Hour)
|
||||
|
||||
// First, get the current values
|
||||
existing, err := impl.GetByUserAndDay(ctx, userID, usageDay)
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to get existing usage for increment",
|
||||
zap.Error(err),
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Time("usage_day", usageDay))
|
||||
return fmt.Errorf("failed to get existing usage: %w", err)
|
||||
}
|
||||
|
||||
// Calculate new values
|
||||
var newTotalBytes, newAddBytes, newRemoveBytes int64
|
||||
if existing != nil {
|
||||
// Add to existing values
|
||||
newTotalBytes = existing.TotalBytes + totalBytes
|
||||
newAddBytes = existing.TotalAddBytes + addBytes
|
||||
newRemoveBytes = existing.TotalRemoveBytes + removeBytes
|
||||
} else {
|
||||
// First record for this day
|
||||
newTotalBytes = totalBytes
|
||||
newAddBytes = addBytes
|
||||
newRemoveBytes = removeBytes
|
||||
}
|
||||
// Insert/Update with the new values
|
||||
query := `
|
||||
INSERT INTO storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
(user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
|
||||
if err := impl.Session.Query(query,
|
||||
userID,
|
||||
usageDay,
|
||||
newTotalBytes,
|
||||
newAddBytes,
|
||||
newRemoveBytes,
|
||||
).WithContext(ctx).Exec(); err != nil {
|
||||
impl.Logger.Error("failed to increment storage daily usage",
|
||||
zap.Error(err),
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Time("usage_day", usageDay))
|
||||
return fmt.Errorf("failed to increment storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
impl.Logger.Debug("storage daily usage incremented",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Time("usage_day", usageDay),
|
||||
zap.Int64("total_bytes_delta", totalBytes),
|
||||
zap.Int64("add_bytes_delta", addBytes),
|
||||
zap.Int64("remove_bytes_delta", removeBytes),
|
||||
zap.Int64("new_total_bytes", newTotalBytes),
|
||||
zap.Int64("new_add_bytes", newAddBytes),
|
||||
zap.Int64("new_remove_bytes", newRemoveBytes))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/delete.go
|
||||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) DeleteByUserAndDay(ctx context.Context, userID gocql.UUID, usageDay time.Time) error {
|
||||
// Ensure usage day is truncated to date only
|
||||
usageDay = usageDay.Truncate(24 * time.Hour)
|
||||
|
||||
query := `DELETE FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
WHERE user_id = ? AND usage_day = ?`
|
||||
|
||||
err := impl.Session.Query(query, userID, usageDay).WithContext(ctx).Exec()
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to delete storage daily usage", zap.Error(err))
|
||||
return fmt.Errorf("failed to delete storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByUserID deletes all storage daily usage records for a user (all days)
|
||||
// Used for GDPR right-to-be-forgotten implementation
|
||||
func (impl *storageDailyUsageRepositoryImpl) DeleteByUserID(ctx context.Context, userID gocql.UUID) error {
|
||||
query := `DELETE FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
WHERE user_id = ?`
|
||||
|
||||
err := impl.Session.Query(query, userID).WithContext(ctx).Exec()
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to delete all storage daily usage for user",
|
||||
zap.String("user_id", userID.String()),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to delete all storage daily usage for user %s: %w", userID.String(), err)
|
||||
}
|
||||
|
||||
impl.Logger.Info("✅ Deleted all storage daily usage records for user",
|
||||
zap.String("user_id", userID.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
221
cloud/maplefile-backend/internal/repo/storagedailyusage/get.go
Normal file
221
cloud/maplefile-backend/internal/repo/storagedailyusage/get.go
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/get.go
|
||||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
|
||||
)
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetByUserAndDay(ctx context.Context, userID gocql.UUID, usageDay time.Time) (*storagedailyusage.StorageDailyUsage, error) {
|
||||
// Ensure usage day is truncated to date only
|
||||
usageDay = usageDay.Truncate(24 * time.Hour)
|
||||
|
||||
var (
|
||||
resultUserID gocql.UUID
|
||||
resultUsageDay time.Time
|
||||
totalBytes int64
|
||||
totalAddBytes int64
|
||||
totalRemoveBytes int64
|
||||
)
|
||||
|
||||
query := `SELECT user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes
|
||||
FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
WHERE user_id = ? AND usage_day = ?`
|
||||
|
||||
err := impl.Session.Query(query, userID, usageDay).WithContext(ctx).Scan(
|
||||
&resultUserID, &resultUsageDay, &totalBytes, &totalAddBytes, &totalRemoveBytes)
|
||||
|
||||
if err == gocql.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to get storage daily usage", zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to get storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
usage := &storagedailyusage.StorageDailyUsage{
|
||||
UserID: resultUserID,
|
||||
UsageDay: resultUsageDay,
|
||||
TotalBytes: totalBytes,
|
||||
TotalAddBytes: totalAddBytes,
|
||||
TotalRemoveBytes: totalRemoveBytes,
|
||||
}
|
||||
|
||||
return usage, nil
|
||||
}
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetByUserDateRange(ctx context.Context, userID gocql.UUID, startDay, endDay time.Time) ([]*storagedailyusage.StorageDailyUsage, error) {
|
||||
// Ensure dates are truncated to date only
|
||||
startDay = startDay.Truncate(24 * time.Hour)
|
||||
endDay = endDay.Truncate(24 * time.Hour)
|
||||
|
||||
query := `SELECT user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes
|
||||
FROM maplefile.storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
WHERE user_id = ? AND usage_day >= ? AND usage_day <= ?`
|
||||
|
||||
iter := impl.Session.Query(query, userID, startDay, endDay).WithContext(ctx).Iter()
|
||||
|
||||
var usages []*storagedailyusage.StorageDailyUsage
|
||||
var (
|
||||
resultUserID gocql.UUID
|
||||
resultUsageDay time.Time
|
||||
totalBytes int64
|
||||
totalAddBytes int64
|
||||
totalRemoveBytes int64
|
||||
)
|
||||
|
||||
for iter.Scan(&resultUserID, &resultUsageDay, &totalBytes, &totalAddBytes, &totalRemoveBytes) {
|
||||
usage := &storagedailyusage.StorageDailyUsage{
|
||||
UserID: resultUserID,
|
||||
UsageDay: resultUsageDay,
|
||||
TotalBytes: totalBytes,
|
||||
TotalAddBytes: totalAddBytes,
|
||||
TotalRemoveBytes: totalRemoveBytes,
|
||||
}
|
||||
usages = append(usages, usage)
|
||||
}
|
||||
|
||||
if err := iter.Close(); err != nil {
|
||||
impl.Logger.Error("failed to get storage daily usage by date range", zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to get storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
return usages, nil
|
||||
}
|
||||
|
||||
// GetLast7DaysTrend retrieves the last 7 days of storage usage and calculates trends
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetLast7DaysTrend(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageTrend, error) {
|
||||
endDay := time.Now().Truncate(24 * time.Hour)
|
||||
startDay := endDay.Add(-6 * 24 * time.Hour) // 7 days including today
|
||||
|
||||
usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return impl.calculateTrend(userID, startDay, endDay, usages), nil
|
||||
}
|
||||
|
||||
// GetMonthlyTrend retrieves usage trend for a specific month
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetMonthlyTrend(ctx context.Context, userID gocql.UUID, year int, month time.Month) (*storagedailyusage.StorageUsageTrend, error) {
|
||||
startDay := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
|
||||
endDay := startDay.AddDate(0, 1, -1) // Last day of the month
|
||||
|
||||
usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return impl.calculateTrend(userID, startDay, endDay, usages), nil
|
||||
}
|
||||
|
||||
// GetYearlyTrend retrieves usage trend for a specific year
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetYearlyTrend(ctx context.Context, userID gocql.UUID, year int) (*storagedailyusage.StorageUsageTrend, error) {
|
||||
startDay := time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
endDay := time.Date(year, 12, 31, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return impl.calculateTrend(userID, startDay, endDay, usages), nil
|
||||
}
|
||||
|
||||
// GetCurrentMonthUsage gets the current month's usage summary
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetCurrentMonthUsage(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageSummary, error) {
|
||||
now := time.Now()
|
||||
startDay := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
|
||||
endDay := now.Truncate(24 * time.Hour)
|
||||
|
||||
usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return impl.calculateSummary(userID, "month", startDay, endDay, usages), nil
|
||||
}
|
||||
|
||||
// GetCurrentYearUsage gets the current year's usage summary
|
||||
func (impl *storageDailyUsageRepositoryImpl) GetCurrentYearUsage(ctx context.Context, userID gocql.UUID) (*storagedailyusage.StorageUsageSummary, error) {
|
||||
now := time.Now()
|
||||
startDay := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
endDay := now.Truncate(24 * time.Hour)
|
||||
|
||||
usages, err := impl.GetByUserDateRange(ctx, userID, startDay, endDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return impl.calculateSummary(userID, "year", startDay, endDay, usages), nil
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) calculateTrend(userID gocql.UUID, startDay, endDay time.Time, usages []*storagedailyusage.StorageDailyUsage) *storagedailyusage.StorageUsageTrend {
|
||||
trend := &storagedailyusage.StorageUsageTrend{
|
||||
UserID: userID,
|
||||
StartDate: startDay,
|
||||
EndDate: endDay,
|
||||
DailyUsages: usages,
|
||||
}
|
||||
|
||||
if len(usages) == 0 {
|
||||
return trend
|
||||
}
|
||||
|
||||
var peakDay time.Time
|
||||
var peakBytes int64
|
||||
|
||||
for _, usage := range usages {
|
||||
trend.TotalAdded += usage.TotalAddBytes
|
||||
trend.TotalRemoved += usage.TotalRemoveBytes
|
||||
|
||||
if usage.TotalBytes > peakBytes {
|
||||
peakBytes = usage.TotalBytes
|
||||
peakDay = usage.UsageDay
|
||||
}
|
||||
}
|
||||
|
||||
trend.NetChange = trend.TotalAdded - trend.TotalRemoved
|
||||
if len(usages) > 0 {
|
||||
trend.AverageDailyAdd = trend.TotalAdded / int64(len(usages))
|
||||
trend.PeakUsageDay = &peakDay
|
||||
trend.PeakUsageBytes = peakBytes
|
||||
}
|
||||
|
||||
return trend
|
||||
}
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) calculateSummary(userID gocql.UUID, period string, startDay, endDay time.Time, usages []*storagedailyusage.StorageDailyUsage) *storagedailyusage.StorageUsageSummary {
|
||||
summary := &storagedailyusage.StorageUsageSummary{
|
||||
UserID: userID,
|
||||
Period: period,
|
||||
StartDate: startDay,
|
||||
EndDate: endDay,
|
||||
DaysWithData: len(usages),
|
||||
}
|
||||
|
||||
if len(usages) == 0 {
|
||||
return summary
|
||||
}
|
||||
|
||||
// Get the most recent usage as current
|
||||
summary.CurrentUsage = usages[len(usages)-1].TotalBytes
|
||||
|
||||
for _, usage := range usages {
|
||||
summary.TotalAdded += usage.TotalAddBytes
|
||||
summary.TotalRemoved += usage.TotalRemoveBytes
|
||||
}
|
||||
|
||||
summary.NetChange = summary.TotalAdded - summary.TotalRemoved
|
||||
|
||||
return summary
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/impl.go
|
||||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
|
||||
)
|
||||
|
||||
type storageDailyUsageRepositoryImpl struct {
|
||||
Logger *zap.Logger
|
||||
Session *gocql.Session
|
||||
}
|
||||
|
||||
func NewRepository(appCfg *config.Configuration, session *gocql.Session, loggerp *zap.Logger) storagedailyusage.StorageDailyUsageRepository {
|
||||
loggerp = loggerp.Named("StorageDailyUsageRepository")
|
||||
|
||||
return &storageDailyUsageRepositoryImpl{
|
||||
Logger: loggerp,
|
||||
Session: session,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
|
||||
)
|
||||
|
||||
// ProvideRepository provides a storage daily usage repository for Wire DI
|
||||
func ProvideRepository(cfg *config.Config, session *gocql.Session, logger *zap.Logger) storagedailyusage.StorageDailyUsageRepository {
|
||||
return NewRepository(cfg, session, logger)
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/storagedailyusage/update.go
|
||||
package storagedailyusage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/storagedailyusage"
|
||||
)
|
||||
|
||||
func (impl *storageDailyUsageRepositoryImpl) UpdateOrCreate(ctx context.Context, usage *storagedailyusage.StorageDailyUsage) error {
|
||||
if usage == nil {
|
||||
return fmt.Errorf("storage daily usage cannot be nil")
|
||||
}
|
||||
|
||||
// Ensure usage day is truncated to date only
|
||||
usage.UsageDay = usage.UsageDay.Truncate(24 * time.Hour)
|
||||
|
||||
// Use UPSERT (INSERT with no IF NOT EXISTS) to update or create
|
||||
query := `INSERT INTO storage_daily_usage_by_user_id_with_asc_usage_day
|
||||
(user_id, usage_day, total_bytes, total_add_bytes, total_remove_bytes)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
|
||||
err := impl.Session.Query(query,
|
||||
usage.UserID,
|
||||
usage.UsageDay,
|
||||
usage.TotalBytes,
|
||||
usage.TotalAddBytes,
|
||||
usage.TotalRemoveBytes,
|
||||
).WithContext(ctx).Exec()
|
||||
|
||||
if err != nil {
|
||||
impl.Logger.Error("failed to upsert storage daily usage", zap.Error(err))
|
||||
return fmt.Errorf("failed to upsert storage daily usage: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue