# frozen_string_literal: true class Site # Obtiene todos los archivos relacionados en artículos del sitio y los # sube a Sutty de forma que los podamos seguir utilizando normalmente # sin casos especiales (ej. soportar archivos locales al repositorio y # remotos, alojados en Sutty) # # TODO: Convertir en reutilizable, por ejemplo correr en cada pull, no # asumir que la migración se hizo una sola vez... class StaticFileMigration # Tipos de metadatos que contienen archivos STATIC_TYPES = %w[file image].freeze attr_reader :site def initialize(site:) @site = site end # Recorre todos los artículos cuyos layouts contengan campos con # archivos estáticos def migrate! log = File.open(File.join(site.path, 'migration.log'), 'w') modified = [] Dir.chdir site.path do site.locales.each do |locale| # Recorrer todos los documentos de todas las colecciones site.posts(lang: locale).each do |doc| # Ignoramos los documentos cuyo layout no contiene archivos next unless layouts.include? doc.layout.name # Buscamos todos los campos con archivos fields.each do |field| next unless doc.attribute? field next unless doc.document.data.key? field.to_s # Traemos los metadatos, en este punto, Sutty cree que el # archivo está subido, porque es una string apuntando a un # archivo. metadata = doc.public_send(field) next if metadata.value['path'].blank? next if ActiveStorage::Blob.find_by(key: metadata.key_from_path) path = Pathname.new(metadata.value['path']) # Si no existe vaciamos el campo unless path.exist? metadata.value['path'] = nil log.write "el archivo #{path} de #{doc.path.relative} no existe" next end # Agregamos el archivo al sitio y se lo asignamos al campo # XXX: No usamos ActionDispatch::Http::UploadedFile porque # no tenemos forma de crear un Tempfile o equivalente a # partir de un archivo que exista. metadata.value['path'] = { io: path.open, filename: path.basename } # Copiar y analizar el archivo sincrónicamente metadata.static_file.blob.upload path.open metadata.static_file.blob.analyze dest = Pathname.new(metadata.send(:relative_destination_path)) metadata.value['path'] = dest.to_s metadata.send(:hardlink) # Eliminamos el archivo original y lo vinculamos al subido # para mantener la ruta y no romper el sitio FileUtils.rm_f path # XXX: Link simbólico o duro? FileUtils.ln_s dest.relative_path_from(path.dirname), path end # Guardamos los cambios log.write "#{doc.path.relative} no se pudo guardar\n" unless doc.save(validate: false) modified << doc.path.absolute end end end log.close return if modified.empty? # TODO: Hacer la migración desde el servicio de creación de sitios? site.repository.commit(file: modified, message: I18n.t('sites.static_file_migration'), usuarie: author) end private def author @author ||= GitAuthor.new email: "sutty@#{Site.domain}", name: 'Sutty' end # Encuentra todos los layouts con campos estáticos def layouts @layouts ||= site.layouts.to_h.reject do |_, layout| layout.metadata.select do |_, desc| STATIC_TYPES.include? desc['type'] end.empty? end.keys end # Encuentra todos los campos con archivos estáticos def fields @fields ||= layouts.map do |layout| site.layouts[layout].metadata.select do |_, desc| STATIC_TYPES.include? desc['type'] end.keys end.flatten.uniq.map(&:to_sym) end end end