Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/dev-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,32 @@ jobs:
id: normalize
run: echo "image_name=$(echo ${{ env.IMAGE_NAME }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT

- name: Get current date
id: date
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT

- name: Create and push manifest
run: |
IMAGE=${{ env.REGISTRY }}/${{ steps.normalize.outputs.image_name }}
AMD64_IMAGE="${IMAGE}:dev-amd64-temp"
ARM64_IMAGE="${IMAGE}:dev-arm64-temp"
DATE=${{ steps.date.outputs.date }}

echo "AMD64 Image: $AMD64_IMAGE"
echo "ARM64 Image: $ARM64_IMAGE"

echo "Creating multi-arch manifest: ${IMAGE}:dev"

docker manifest create ${IMAGE}:dev \
$AMD64_IMAGE \
$ARM64_IMAGE
docker manifest push ${IMAGE}:dev

echo "Creating multi-arch manifest: ${IMAGE}:dev-${DATE}"
docker manifest create ${IMAGE}:dev-${DATE} \
$AMD64_IMAGE \
$ARM64_IMAGE
docker manifest push ${IMAGE}:dev-${DATE}

- name: Clean up temporary tags
run: |
IMAGE=${{ env.REGISTRY }}/${{ steps.normalize.outputs.image_name }}
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/manual-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,41 +244,55 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Get current date
id: date
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT

# Create sanitized branch name for tag
- name: Create sanitized tag
id: tag
run: |
# Replace / with - and remove other special characters
SAFE_BRANCH=$(echo "${{ inputs.branch }}" | sed 's/\//-/g' | sed 's/[^a-zA-Z0-9._-]/-/g')
DATE=${{ steps.date.outputs.date }}
TAG="${SAFE_BRANCH}-${{ inputs.tag_suffix }}"
DATED_TAG="${TAG}-${DATE}"
AMD64_TAG="${TAG}-amd64"
ARM64_TAG="${TAG}-arm64"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "dated_tag=$DATED_TAG" >> $GITHUB_OUTPUT
echo "amd64_tag=$AMD64_TAG" >> $GITHUB_OUTPUT
echo "arm64_tag=$ARM64_TAG" >> $GITHUB_OUTPUT
echo "Creating multi-arch manifest with tag: $TAG"
echo "Creating multi-arch manifest with tags: $TAG, $DATED_TAG"

# Create and push multi-platform manifest
- name: Create multi-platform manifest
run: |
AMD64_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.amd64_tag }}"
ARM64_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.arm64_tag }}"
MANIFEST_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.tag }}"
DATED_MANIFEST_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.dated_tag }}"

echo "AMD64 Image: $AMD64_IMAGE"
echo "ARM64 Image: $ARM64_IMAGE"
echo "Manifest Tag: $MANIFEST_TAG"
echo "Dated Manifest Tag: $DATED_MANIFEST_TAG"

# Create and push multi-arch manifest
docker manifest create $MANIFEST_TAG $AMD64_IMAGE $ARM64_IMAGE
docker manifest push $MANIFEST_TAG

# Create and push dated multi-arch manifest
docker manifest create $DATED_MANIFEST_TAG $AMD64_IMAGE $ARM64_IMAGE
docker manifest push $DATED_MANIFEST_TAG

# Output manifest details
- name: Manifest created successfully
run: |
echo "✅ Multi-architecture Docker manifest created successfully!"
echo ""
echo "📦 Multi-arch image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.tag }}"
echo "📦 Dated multi-arch image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.dated_tag }}"
echo "📦 AMD64 image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.amd64_tag }}"
echo "📦 ARM64 image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ steps.tag.outputs.arm64_tag }}"
echo ""
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,17 @@ jobs:
echo "major=$MAJOR" >> $GITHUB_OUTPUT
echo "minor=$MINOR" >> $GITHUB_OUTPUT

- name: Get current date
id: date
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT

# Create and push multi-platform manifests using temporary tags
- name: Create multi-platform manifest
run: |
# Use temporary tagged images instead of digests
AMD64_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v${{ steps.version.outputs.version }}-amd64-temp"
ARM64_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v${{ steps.version.outputs.version }}-arm64-temp"
DATE=${{ steps.date.outputs.date }}

echo "AMD64 Image: $AMD64_IMAGE"
echo "ARM64 Image: $ARM64_IMAGE"
Expand Down Expand Up @@ -516,6 +521,13 @@ jobs:
$ARM64_IMAGE
docker manifest push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:latest

# Create and push dated latest manifest (multi-arch)
docker manifest create \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:latest-${DATE} \
$AMD64_IMAGE \
$ARM64_IMAGE
docker manifest push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:latest-${DATE}

# Clean up temporary tags
- name: Clean up temporary tags
run: |
Expand Down
2 changes: 1 addition & 1 deletion cmd/altmount/cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func runServe(cmd *cobra.Command, args []string) error {
}()

repos := setupRepositories(ctx, db)
poolManager := pool.NewManager(ctx)
poolManager := pool.NewManager(ctx, repos.MainRepo)

metadataService, metadataReader := initializeMetadata(cfg)

Expand Down
1 change: 1 addition & 0 deletions internal/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ func (s *Server) SetupRoutes(app *fiber.App) {
api.Get("/system/health", s.handleGetSystemHealth)
api.Get("/system/browse", s.handleSystemBrowse)
api.Get("/system/pool/metrics", s.handleGetPoolMetrics)
api.Post("/system/stats/reset", s.handleResetSystemStats)
api.Post("/system/cleanup", s.handleSystemCleanup)
api.Post("/system/restart", s.handleSystemRestart)

Expand Down
19 changes: 19 additions & 0 deletions internal/api/system_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ func (s *Server) handleSystemRestart(c *fiber.Ctx) error {
return result
}

// handleResetSystemStats handles POST /api/system/stats/reset
func (s *Server) handleResetSystemStats(c *fiber.Ctx) error {
// Reset pool metrics
if s.poolManager != nil {
if err := s.poolManager.ResetMetrics(c.Context()); err != nil {
return c.Status(500).JSON(fiber.Map{
"success": false,
"message": "Failed to reset pool metrics",
"details": err.Error(),
})
}
}

return c.Status(200).JSON(fiber.Map{
"success": true,
"message": "System statistics reset successfully",
})
}

// performRestart performs the actual server restart
func (s *Server) performRestart(ctx context.Context) {
slog.InfoContext(ctx, "Initiating server restart process")
Expand Down
84 changes: 84 additions & 0 deletions internal/database/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,90 @@ func (r *Repository) GetSystemState(ctx context.Context, key string) (string, er
return value, nil
}

// UpdateSystemStat updates a numeric system statistic by key
func (r *Repository) UpdateSystemStat(ctx context.Context, key string, value int64) error {
query := `
INSERT INTO system_stats (key, value, updated_at)
VALUES (?, ?, datetime('now'))
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = datetime('now')
`
_, err := r.db.ExecContext(ctx, query, key, value)
if err != nil {
return fmt.Errorf("failed to update system stat %s: %w", key, err)
}
return nil
}

// BatchUpdateSystemStats updates multiple numeric system statistics in a single transaction
func (r *Repository) BatchUpdateSystemStats(ctx context.Context, stats map[string]int64) error {
if len(stats) == 0 {
return nil
}

// Cast to *sql.DB to access BeginTx method
sqlDB, ok := r.db.(*sql.DB)
if !ok {
// If we're already in a transaction, we can just execute the statements
// However, it's better to provide a consistent way to handle this
return fmt.Errorf("repository not connected to sql.DB, cannot begin transaction")
}

tx, err := sqlDB.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()

query := `
INSERT INTO system_stats (key, value, updated_at)
VALUES (?, ?, datetime('now'))
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = datetime('now')
`

stmt, err := tx.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()

for key, value := range stats {
if _, err := stmt.ExecContext(ctx, key, value); err != nil {
return fmt.Errorf("failed to execute statement for key %s: %w", key, err)
}
}

if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}

return nil
}

// GetSystemStats retrieves all numeric system statistics
func (r *Repository) GetSystemStats(ctx context.Context) (map[string]int64, error) {
query := `SELECT key, value FROM system_stats`
rows, err := r.db.QueryContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("failed to get system stats: %w", err)
}
defer rows.Close()

stats := make(map[string]int64)
for rows.Next() {
var key string
var value int64
if err := rows.Scan(&key, &value); err != nil {
return nil, fmt.Errorf("failed to scan system stat: %w", err)
}
stats[key] = value
}
return stats, nil
}

// GetImportHistory retrieves historical import statistics for the last N days
func (r *Repository) GetImportHistory(ctx context.Context, days int) ([]*ImportDailyStat, error) {
query := `
Expand Down
Loading
Loading