diff --git a/app/models/indexed_post.rb b/app/models/indexed_post.rb index 7f6865f6..c401f764 100644 --- a/app/models/indexed_post.rb +++ b/app/models/indexed_post.rb @@ -35,6 +35,7 @@ class IndexedPost < ApplicationRecord scope :by_usuarie, ->(usuarie) { where("front_matter->'usuaries' @> :usuarie::jsonb", usuarie: usuarie.to_s) } belongs_to :site + has_many :permalinks, as: :permalinkeable # Convertir locale a direccionario de PG # diff --git a/app/models/permalink.rb b/app/models/permalink.rb new file mode 100644 index 00000000..3cb32d11 --- /dev/null +++ b/app/models/permalink.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# Lleva un registro de los permalinks de cada sitio y artículo +class Permalink < ApplicationRecord + belongs_to :permalinkeable, polymorphic: true + belongs_to :site + + # La URL no se puede repetir dentro del sitio, pero puede existir la + # misma dentro de otro. + validates :url, presence: true, uniqueness: { scope: :site } + + # Para poder traer todas las visitas de este post necesitamos filtrar + # primero por los hosts del sitio. + # + # TODO: Cambiar a request_uri para no tener que usar path + # TODO: Esto necesita que mergeemos origin-referer para tener acceso + # a los hostnames. + # + # @return [ActiveRecord::Relation] + def access_logs + AccessLog.where(host: site.hostnames).where(uri: [url, path]) + end + + # XXX: Esto podría servir para traer propiedades del archivo final en + # el sitio? Como espacio ocupado, tasa de compresión... + # + # @return [String] + def path + url.end_with?('/') ? "#{url}index.html" : url + end +end diff --git a/db/migrate/20210926172408_create_permalinks.rb b/db/migrate/20210926172408_create_permalinks.rb new file mode 100644 index 00000000..0bf92005 --- /dev/null +++ b/db/migrate/20210926172408_create_permalinks.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Crea la tabla de permalinks que lleva el registro de todas las URLs +# posibles de un artículo. +# +# A través de los permalinks podemos: +# +# * Mantener un listado estable de todas las rutas de un artículo y +# llevar el listado de alternate-permalinks, para nunca romper un +# permalink. +# +# * Relacionar artículos con sus visitas. +# +# * Llevar un registro de todos los archivos estáticos (imágenes, audio, +# video, documentos) y sus descargas. +class CreatePermalinks < ActiveRecord::Migration[6.1] + def up + create_table :permalinks, id: false do |t| + t.primary_key :id, :uuid, default: 'public.gen_random_uuid()' + t.belongs_to :site, null: false, index: true + t.string :permalinkeable_type, null: false + t.string :permalinkeable_id, null: false + t.string :url, null: false + t.timestamps + end + + add_index :permalinks, %i[permalinkeable_type permalinkeable_id] + add_index :permalinks, %i[site_id url], unique: true + + # XXX: Esta migración va a ignorar las URLs duplicadas. + Site.find_each do |site| + next unless site.jekyll + + site.docs.each do |post| + post.to_index.tap do |indexed_post| + indexed_post.permalinks.build url: post.document.url, site: site + end.save + end + end + end + + def down + drop_table :permalinks + end +end diff --git a/db/schema.rb b/db/schema.rb index 107e7be7..8ab7b8de 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -291,6 +291,18 @@ ActiveRecord::Schema.define(version: 2021_05_14_165639) do t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_text_translations_on_keys", unique: true end + create_table "permalinks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t| + t.bigint "site_id", null: false + t.string "permalinkeable_type", null: false + t.string "permalinkeable_id", null: false + t.string "url", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["permalinkeable_type", "permalinkeable_id"], name: "index_permalinks_on_permalinkeable_type_and_permalinkeable_id" + t.index ["site_id"], name: "index_permalinks_on_site_id" + t.index ["url"], name: "index_permalinks_on_url", unique: true + end + create_table "roles", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false