Add admin area
This commit is contained in:
@@ -29,6 +29,9 @@ html
|
||||
|
||||
script src="https://unpkg.com/htmx.org@1.8.4" integrity="sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV" crossorigin="anonymous"
|
||||
|
||||
script src="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"
|
||||
link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet"
|
||||
|
||||
- if Hanami.app.settings.micropub_pub_key
|
||||
link rel="pgpkey" href="/key"
|
||||
body class="bg-white dark:bg-black selection:bg-blue-100 selection:text-blue-900 dark:selection:bg-blue-600 dark:selection:text-blue-100"
|
||||
|
@@ -6,10 +6,13 @@ module Adamantium
|
||||
class App < Hanami::App
|
||||
config.actions.content_security_policy[:script_src] += " https://gist.github.com"
|
||||
config.actions.content_security_policy[:script_src] += " *.dnitza.com"
|
||||
config.actions.content_security_policy[:script_src] += " https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js"
|
||||
config.actions.content_security_policy[:media_src] += " https://dnitza.com"
|
||||
config.actions.content_security_policy[:script_src] += " https://unpkg.com/htmx.org@1.8.4"
|
||||
config.actions.content_security_policy[:connect_src] += " https://stats.dnitza.com/api/event"
|
||||
config.actions.content_security_policy[:script_src] += " https://unpkg.com/htmx.org@1.8.4 https://unpkg.com/htmx.org@1.9.2"
|
||||
config.actions.content_security_policy[:connect_src] += " https://stats.dnitza.com/api/event https://*.mapbox.com"
|
||||
config.actions.content_security_policy[:frame_src] += " https://embed.music.apple.com"
|
||||
config.actions.content_security_policy[:style_src] += " https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css"
|
||||
config.actions.content_security_policy[:child_src] = " blob:"
|
||||
|
||||
config.logger.level = :debug
|
||||
config.logger.stream = "log/hanami.log"
|
||||
|
@@ -1,12 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "hanami/middleware/body_parser"
|
||||
require "adamantium/middleware/process_params"
|
||||
require "adamantium/middleware/basic_auth"
|
||||
|
||||
module Adamantium
|
||||
class Routes < Hanami::Routes
|
||||
use Hanami::Middleware::BodyParser, [:form, :json]
|
||||
# use Adamantium::Middleware::ProcessParams
|
||||
if Hanami.app.settings.basic_auth_username && Hanami.app.settings.basic_auth_password
|
||||
use Adamantium::Middleware::BasicAuth do |username, password|
|
||||
username == Hanami.app.settings.basic_auth_username &&
|
||||
password == Hanami.app.settings.basic_auth_password
|
||||
end
|
||||
end
|
||||
|
||||
scope "micropub" do
|
||||
get "/", to: "site.config"
|
||||
@@ -48,5 +54,12 @@ module Adamantium
|
||||
|
||||
redirect "deploying-a-hanami-app-to-fly-io", to: "/post/deploying-a-hanami-20-app-to-flyio"
|
||||
redirect "deploying-a-hanami-app-to-fly-io/", to: "/post/deploying-a-hanami-20-app-to-flyio"
|
||||
|
||||
slice :admin, at: "/admin" do
|
||||
get "/", to: "index"
|
||||
|
||||
get "/tags", to: "tags.index"
|
||||
delete "/tags/:id", to: "tags.delete"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -43,5 +43,8 @@ module Adamantium
|
||||
setting :mastodon_server, default: nil
|
||||
|
||||
setting :raindrop_api_key, default: nil
|
||||
|
||||
setting :basic_auth_username, default: nil
|
||||
setting :basic_auth_password, default: nil
|
||||
end
|
||||
end
|
||||
|
@@ -0,0 +1,16 @@
|
||||
module Adamantium
|
||||
module Middleware
|
||||
class BasicAuth < Rack::Auth::Basic
|
||||
def call(env)
|
||||
request = Rack::Request.new(env)
|
||||
if request.path.match(/^\/admin*/)
|
||||
# Execute basic authentication
|
||||
super(env)
|
||||
else
|
||||
# Pass basic authentication
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -6,8 +6,8 @@ module Adamantium
|
||||
class Tags < ROM::Relation[:sql]
|
||||
schema :tags, infer: true do
|
||||
associations do
|
||||
belongs_to :post_tag
|
||||
belongs_to :post, through: :post_tag
|
||||
has_many :post_tags
|
||||
has_many :posts, through: :post_tags
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -5,5 +5,19 @@
|
||||
const oldDtime = Date.parse(time.dateTime);
|
||||
time.innerHTML = new Date(oldDtime).toLocaleDateString(navigator.language, { weekday:"long", year:"numeric", month:"short", day:"numeric"});
|
||||
});
|
||||
|
||||
// mapboxgl.accessToken = 'pk.eyJ1IjoiZG5pdHphIiwiYSI6ImNsZWIyY3ZzaTE0cjUzdm4xdnZ6czRlYjUifQ.FRETOXYRID6T2IoB7qqRLg';
|
||||
// var map = new mapboxgl.Map({
|
||||
// container: 'map',
|
||||
// style: 'mapbox://styles/mapbox/streets-v11'
|
||||
// });
|
||||
// const mapContainer = document.getElementById("map");
|
||||
// const markers = JSON.parse(mapContainer.dataset["markers"]);
|
||||
// for (var i = 0; i < markers.length; i++) {
|
||||
// const marker = markers[i];
|
||||
// new mapboxgl.Marker()
|
||||
// .setLngLat(marker)
|
||||
// .addTo(map);
|
||||
// }
|
||||
});
|
||||
})();
|
||||
|
7
slices/admin/action.rb
Normal file
7
slices/admin/action.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# auto_register: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class Action < Adamantium::Action
|
||||
end
|
||||
end
|
0
slices/admin/actions/.keep
Normal file
0
slices/admin/actions/.keep
Normal file
12
slices/admin/actions/index.rb
Normal file
12
slices/admin/actions/index.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
module Admin
|
||||
module Actions
|
||||
class Index < Action
|
||||
|
||||
include Deps["views.index"]
|
||||
|
||||
def handle(req, res)
|
||||
res.render index
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
slices/admin/actions/tags/delete.rb
Normal file
17
slices/admin/actions/tags/delete.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module Admin
|
||||
module Actions
|
||||
module Tags
|
||||
class Delete < Action
|
||||
|
||||
include Deps["repos.post_tag_repo", "repos.tag_repo"]
|
||||
|
||||
def handle(req, res)
|
||||
tag_id = req.params[:id]
|
||||
|
||||
post_tag_repo.delete(tag_id: tag_id)
|
||||
tag_repo.delete(tag_id: tag_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
14
slices/admin/actions/tags/index.rb
Normal file
14
slices/admin/actions/tags/index.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module Admin
|
||||
module Actions
|
||||
module Tags
|
||||
class Index < Action
|
||||
|
||||
include Deps["views.tags.index"]
|
||||
|
||||
def handle(req, res)
|
||||
res.render index
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
11
slices/admin/repos/post_tag_repo.rb
Normal file
11
slices/admin/repos/post_tag_repo.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
module Admin
|
||||
module Repos
|
||||
class PostTagRepo < Adamantium::Repo[:post_tags]
|
||||
|
||||
def delete(tag_id:)
|
||||
post_tags.where(tag_id: tag_id).delete
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
16
slices/admin/repos/tag_repo.rb
Normal file
16
slices/admin/repos/tag_repo.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
module Admin
|
||||
module Repos
|
||||
class TagRepo < Adamantium::Repo[:tags]
|
||||
def list
|
||||
tags
|
||||
.combine(:posts)
|
||||
.order(Sequel.function(:lower, :label))
|
||||
.to_a
|
||||
end
|
||||
|
||||
def delete(tag_id:)
|
||||
tags.by_pk(tag_id).delete
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
12
slices/admin/templates/index.html.slim
Normal file
12
slices/admin/templates/index.html.slim
Normal file
@@ -0,0 +1,12 @@
|
||||
div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200"
|
||||
h1 Admin
|
||||
|
||||
div class="max-w-prose mx-auto"
|
||||
ul
|
||||
li
|
||||
a href="/admin/tags" Tags
|
||||
|
||||
div class="max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600"
|
||||
|
||||
|
||||
|
56
slices/admin/templates/layouts/app.html.slim
Normal file
56
slices/admin/templates/layouts/app.html.slim
Normal file
@@ -0,0 +1,56 @@
|
||||
html
|
||||
head
|
||||
meta charest="utf-8"
|
||||
|
||||
meta name="viewport" content="width=device-width, initial-scale=1.0"
|
||||
|
||||
meta name="theme-color" content="rgb(37, 99, 235)"
|
||||
|
||||
title Daniel Nitsikopoulos
|
||||
|
||||
link rel="authorization_endpoint" href=Hanami.app.settings.micropub_authorization_endpoint
|
||||
link rel="token_endpoint" href=Hanami.app.settings.micropub_token_endpoint
|
||||
link rel="micropub" href="#{URI.join(Hanami.app.settings.micropub_site_url, "micropub")}"
|
||||
|
||||
link rel="webmention" href=Hanami.app.settings.webmention_url
|
||||
link rel="pingback" href=Hanami.app.settings.pingback_url
|
||||
link rel="feed" type="text/html" href="/posts"
|
||||
link rel="feed alternate" type="application/rss+xml" href="/feeds/rss"
|
||||
link rel="feed alternate" type="application/rss+xml" href="/feeds/statuses_rss"
|
||||
|
||||
link rel="me" href=Hanami.app.settings.mastodon_url
|
||||
link rel="me" href=Hanami.app.settings.github_url
|
||||
|
||||
link rel="stylesheet" href="/assets/index.css"
|
||||
link rel="icon" type="image/x-icon" href="/assets/favicon.ico"
|
||||
|
||||
script data-domain="dnitza.com" src="https://stats.dnitza.com/js/script.js" defer="true"
|
||||
script src="/assets/index.js"
|
||||
|
||||
script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"
|
||||
|
||||
- if Hanami.app.settings.micropub_pub_key
|
||||
link rel="pgpkey" href="/key"
|
||||
body class="bg-white dark:bg-black selection:bg-blue-100 selection:text-blue-900 dark:selection:bg-blue-600 dark:selection:text-blue-100"
|
||||
main class="pb-8 px-4 pt-4 md:pt-8"
|
||||
header class="mb-12 max-w-screen-md mx-auto"
|
||||
div class="flex items-center mb-8 md:mb-12 text-lg md:text-xl text-gray-400 dark:text-gray-600"
|
||||
div class="flex-none mx-auto md:flex-auto md:mx-0"
|
||||
div class="h-card flex items-center"
|
||||
img class="u-photo w-6 h6 md:w-10 md:h-10 rounded-full mr-1.5" src="/assets/memoji.png"
|
||||
a href="/" rel="me" class="u-url u-uid"
|
||||
h1 class="p-name uppercase text-sm md:text-sm text-gray-400 dark:text-gray-400" = Hanami.app.settings.site_name
|
||||
nav class="space-x-1 text-sm md:text-sm uppercase md:block"
|
||||
a class="p-1 rounded text-gray-400 hover:bg-red-100 hover:text-red-400 dark:hover:bg-red-200 #{link_active?('about') ? 'text-red-600 dark:text-red-400' : ''}" href="/admin" Admin
|
||||
span class="text-gray-400 dark:text-gray-600"
|
||||
= "/"
|
||||
a class="p-1 rounded text-gray-400 hover:bg-red-100 hover:text-red-400 dark:hover:bg-red-200 #{link_active?('about') ? 'text-red-600 dark:text-red-400' : ''}" href="/admin/tags" Tags
|
||||
span class="text-gray-400 dark:text-gray-600"
|
||||
= "/"
|
||||
== yield
|
||||
div class="px-4 max-w-screen-md mx-auto pb-10"
|
||||
p class="float-left text-gray-200 dark:text-gray-600" © 2023 Daniel Nitsikopoulos. All rights reserved.
|
||||
p class="float-right text-gray-200 dark:text-gray-600"
|
||||
a href="https://xn--sr8hvo.ws/%F0%9F%8D%93%E2%9E%97%F0%9F%8E%B0/previous" ←
|
||||
a href="https://xn--sr8hvo.ws" 🕸💍
|
||||
a href="https://xn--sr8hvo.ws/%F0%9F%8D%93%E2%9E%97%F0%9F%8E%B0/next" →
|
4
slices/admin/templates/shared/_tag.html.slim
Normal file
4
slices/admin/templates/shared/_tag.html.slim
Normal file
@@ -0,0 +1,4 @@
|
||||
li id="tag-#{tag.id}"
|
||||
= "#{tag.label} (#{tag.posts.count})"
|
||||
= " — "
|
||||
button hx-delete="/admin/tags/#{tag.id}" hx-target="#tag-#{tag.id}" delete
|
17
slices/admin/templates/tags/index.html.slim
Normal file
17
slices/admin/templates/tags/index.html.slim
Normal file
@@ -0,0 +1,17 @@
|
||||
div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200"
|
||||
h1 Admin // Tags
|
||||
|
||||
div class="max-w-prose mx-auto"
|
||||
ul
|
||||
- unused_tags.each do |tag|
|
||||
== render "shared/tag", tag: tag
|
||||
li class="py-2"
|
||||
hr
|
||||
- used_tags.each do |tag|
|
||||
== render "shared/tag", tag: tag
|
||||
|
||||
|
||||
div class="max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600"
|
||||
|
||||
|
||||
|
6
slices/admin/view.rb
Normal file
6
slices/admin/view.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Admin
|
||||
class View < Adamantium::View
|
||||
config.layouts_dir = "layouts"
|
||||
config.paths = "slices/admin/templates"
|
||||
end
|
||||
end
|
7
slices/admin/views/index.rb
Normal file
7
slices/admin/views/index.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Admin
|
||||
module Views
|
||||
class Index < Admin::View
|
||||
|
||||
end
|
||||
end
|
||||
end
|
22
slices/admin/views/tags/index.rb
Normal file
22
slices/admin/views/tags/index.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
module Admin
|
||||
module Views
|
||||
module Tags
|
||||
class Index < Admin::View
|
||||
|
||||
include Deps["repos.tag_repo"]
|
||||
|
||||
expose :tags do
|
||||
tag_repo.list.to_a
|
||||
end
|
||||
|
||||
expose :unused_tags do |tags|
|
||||
tags.partition {|t| t.posts.count == 0}.first
|
||||
end
|
||||
|
||||
expose :used_tags do |tags|
|
||||
tags.partition {|t| t.posts.count == 0}.last
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user