Styled landing, Bluesky and Fedi redirect pages.
This commit is contained in:
parent
7e75a71bd2
commit
b6f5a4e0cc
24 changed files with 406 additions and 132 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
20250226.1
|
20250226.2
|
||||||
|
|
|
@ -18,10 +18,13 @@ const MszLoadingIcon = function() {
|
||||||
let tsLastUpdate;
|
let tsLastUpdate;
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
let playing = false;
|
let playing = false;
|
||||||
|
let delay = 50;
|
||||||
|
let playResolve;
|
||||||
|
let pauseResolve;
|
||||||
|
|
||||||
const update = tsCurrent => {
|
const update = tsCurrent => {
|
||||||
try {
|
try {
|
||||||
if(tsLastUpdate !== undefined && (tsCurrent - tsLastUpdate) < 50)
|
if(tsLastUpdate !== undefined && (tsCurrent - tsLastUpdate) < delay)
|
||||||
return;
|
return;
|
||||||
tsLastUpdate = tsCurrent;
|
tsLastUpdate = tsCurrent;
|
||||||
|
|
||||||
|
@ -30,29 +33,94 @@ const MszLoadingIcon = function() {
|
||||||
|
|
||||||
++counter;
|
++counter;
|
||||||
} finally {
|
} finally {
|
||||||
|
if(playResolve)
|
||||||
|
try {
|
||||||
|
playResolve();
|
||||||
|
} finally {
|
||||||
|
playResolve = undefined;
|
||||||
|
playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pauseResolve)
|
||||||
|
try {
|
||||||
|
pauseResolve();
|
||||||
|
} finally {
|
||||||
|
pauseResolve = undefined;
|
||||||
|
playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(playing)
|
if(playing)
|
||||||
requestAnimationFrame(update);
|
requestAnimationFrame(update);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const play = () => {
|
const play = () => {
|
||||||
if(playing)
|
return new Promise(resolve => {
|
||||||
|
if(playing || playResolve) {
|
||||||
|
resolve();
|
||||||
return;
|
return;
|
||||||
playing = true;
|
}
|
||||||
|
|
||||||
|
playResolve = resolve;
|
||||||
requestAnimationFrame(update);
|
requestAnimationFrame(update);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const pause = () => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if(!playing || pauseResolve) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pauseResolve = resolve;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const stop = async () => {
|
||||||
|
await pause();
|
||||||
|
counter = 0;
|
||||||
|
};
|
||||||
|
const restart = async () => {
|
||||||
|
await stop();
|
||||||
|
await play();
|
||||||
|
};
|
||||||
|
const reverse = () => {
|
||||||
|
blocks.reverse();
|
||||||
|
};
|
||||||
|
const setBlock = (num, state=null) => {
|
||||||
|
element.children[num].classList.toggle('msz-loading-icon-block-hidden', !state);
|
||||||
|
};
|
||||||
|
const batsu = () => {
|
||||||
|
setBlock(0, true);setBlock(1, false);setBlock(2, true);
|
||||||
|
setBlock(3, false);setBlock(4, true);setBlock(5, false);
|
||||||
|
setBlock(6, true);setBlock(7, false);setBlock(8, true);
|
||||||
|
};
|
||||||
|
const maru = () => {
|
||||||
|
setBlock(0, true);setBlock(1, true);setBlock(2, true);
|
||||||
|
setBlock(3, true);setBlock(4, false);setBlock(5, true);
|
||||||
|
setBlock(6, true);setBlock(7, true);setBlock(8, true);
|
||||||
};
|
};
|
||||||
const pause = () => { playing = false; };
|
|
||||||
const stop = () => { pause(); counter = 0; };
|
|
||||||
const restart = () => { stop(); play(); };
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get element() { return element; },
|
get element() { return element; },
|
||||||
get playing() { return playing; },
|
get playing() { return playing; },
|
||||||
|
get delay() { return delay; },
|
||||||
|
set delay(value) {
|
||||||
|
if(typeof value !== 'number')
|
||||||
|
value = parseFloat(value);
|
||||||
|
if(isNaN(value) || !isFinite(value))
|
||||||
|
return;
|
||||||
|
if(value < 0)
|
||||||
|
value = Math.abs(value);
|
||||||
|
delay = value;
|
||||||
|
},
|
||||||
|
|
||||||
play: play,
|
play,
|
||||||
pause: pause,
|
pause,
|
||||||
stop: stop,
|
stop,
|
||||||
restart: restart,
|
restart,
|
||||||
|
reverse,
|
||||||
|
batsu,
|
||||||
|
maru,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
mask-image: linear-gradient(0deg, transparent 10%, var(--background-colour) 100%);
|
mask-image: linear-gradient(0deg, transparent 10%, var(--background-colour) 100%);
|
||||||
-webkit-mask-image: linear-gradient(0deg, transparent 10%, var(--background-colour) 100%);
|
|
||||||
background: var(--background-pattern);
|
background: var(--background-pattern);
|
||||||
background-color: var(--accent-colour);
|
background-color: var(--accent-colour);
|
||||||
background-blend-mode: multiply;
|
background-blend-mode: multiply;
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
(async () => {
|
|
||||||
const status = document.querySelector('.js-status');
|
|
||||||
status.textContent = 'Looking up DID...';
|
|
||||||
|
|
||||||
let did = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${location.protocol}//${BSKY_HANDLE}/.well-known/atproto-did`);
|
|
||||||
did = await response.text();
|
|
||||||
} catch(ex) {
|
|
||||||
status.style.color = 'red';
|
|
||||||
status.textContent = `Could not find DID! ${ex}`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof did !== 'string' || !did.startsWith('did:')) {
|
|
||||||
status.style.color = 'red';
|
|
||||||
status.textContent = 'Look up result was not a valid DID.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = BSKY_FORMAT.replace('%s', did);
|
|
||||||
status.textContent = `Redirecting to ${url}...`;
|
|
||||||
location.replace(url);
|
|
||||||
})();
|
|
|
@ -1,39 +0,0 @@
|
||||||
(async () => {
|
|
||||||
const status = document.querySelector('.js-status');
|
|
||||||
status.textContent = 'Looking up Fediverse profile...';
|
|
||||||
|
|
||||||
let url = null;
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${location.protocol}//${FEDI_INSTANCE}/.well-known/webfinger?format=json&resource=acct:${FEDI_USERNAME}@${FEDI_INSTANCE}`);
|
|
||||||
const webfinger = await response.json();
|
|
||||||
|
|
||||||
if(typeof webfinger === 'object') {
|
|
||||||
if(Array.isArray(webfinger.links))
|
|
||||||
for(const link of webfinger.links)
|
|
||||||
if(typeof link === 'object' && link.rel === 'http://webfinger.net/rel/profile-page' && typeof link.href === 'string') {
|
|
||||||
url = link.href;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof url !== 'string' && Array.isArray(webfinger.aliases))
|
|
||||||
for(const alias of webfinger.aliases)
|
|
||||||
if(typeof alias === 'string') {
|
|
||||||
url = alias;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
status.style.color = 'red';
|
|
||||||
status.textContent = `Could not complete Webfinger lookup! ${ex}`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof url !== 'string' || (!url.startsWith('https://') && !url.startsWith('http://'))) {
|
|
||||||
status.style.color = 'red';
|
|
||||||
status.textContent = 'Could not find an acceptable profile URL.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.textContent = `Redirecting to ${url}...`;
|
|
||||||
location.replace(url);
|
|
||||||
})();
|
|
42
assets/redirects.css/landing.css
Normal file
42
assets/redirects.css/landing.css
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
.redir-landing {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-landing-content {
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
background: #191919;
|
||||||
|
box-shadow: 0 1px 2px #0009;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-landing-body {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.redir-landing-body p {
|
||||||
|
margin: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-landing-footer {
|
||||||
|
font-size: .8em;
|
||||||
|
line-height: 1.4em;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-landing-logo {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #fff;
|
||||||
|
mask: url('/images/flashii.svg') no-repeat center;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
margin: 10px;
|
||||||
|
font-size: 0;
|
||||||
|
}
|
54
assets/redirects.css/main.css
Normal file
54
assets/redirects.css/main.css
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
:root {
|
||||||
|
--accent-colour: #8559a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #111;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-family: var(--font-regular);
|
||||||
|
overflow-y: scroll;
|
||||||
|
position: static;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #1e90ff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: #6B4F80;
|
||||||
|
}
|
||||||
|
a:hover,
|
||||||
|
a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
mask-image: linear-gradient(315deg, #000f 0, #0000 40%);
|
||||||
|
background: url('/images/clouds.png');
|
||||||
|
background-color: var(--accent-colour);
|
||||||
|
background-blend-mode: multiply;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-foreground {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include landing.css;
|
||||||
|
@include social.css;
|
26
assets/redirects.css/social.css
Normal file
26
assets/redirects.css/social.css
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
.redir-social {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-social-content {
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
background: #191919;
|
||||||
|
box-shadow: 0 1px 2px #0009;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redir-social-body {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.redir-social-body p {
|
||||||
|
margin: .5em;
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
32
assets/redirects.js/bsky.jsx
Normal file
32
assets/redirects.js/bsky.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
const MszRedirectsBsky = async () => {
|
||||||
|
const loading = new MszLoading({ element: '.js-loading', size: 2, inline: true });
|
||||||
|
|
||||||
|
const statusBig = document.querySelector('.js-status-big');
|
||||||
|
const statusSmall = document.querySelector('.js-status-small');
|
||||||
|
const setStatusSmall = body => {
|
||||||
|
$removeChildren(statusSmall);
|
||||||
|
$appendChild(statusSmall, body);
|
||||||
|
};
|
||||||
|
|
||||||
|
setStatusSmall(<>Resolving ATProto DID for @{BSKY_HANDLE}...</>);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { body } = await $xhr.get(`${location.protocol}//${BSKY_HANDLE}/.well-known/atproto-did`);
|
||||||
|
if(typeof body !== 'string' || !body.startsWith('did:'))
|
||||||
|
throw new Error('Was unable to resolve the given handle.');
|
||||||
|
|
||||||
|
const url = BSKY_FORMAT.replace('%s', body);
|
||||||
|
setStatusSmall(<>Redirecting to <a href={url} rel="noopener">{url}</a>...</>);
|
||||||
|
location.replace(url);
|
||||||
|
} catch(ex) {
|
||||||
|
await loading.icon.stop();
|
||||||
|
loading.icon.batsu();
|
||||||
|
|
||||||
|
let errorText = ex.toString();
|
||||||
|
if(errorText.startsWith('['))
|
||||||
|
errorText = 'Something went wrong.';
|
||||||
|
|
||||||
|
statusBig.textContent = 'Profile not found!';
|
||||||
|
setStatusSmall(<span style="color: red">{errorText}</span>);
|
||||||
|
}
|
||||||
|
};
|
49
assets/redirects.js/fedi.jsx
Normal file
49
assets/redirects.js/fedi.jsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
const MszRedirectsFedi = async () => {
|
||||||
|
const loading = new MszLoading({ element: '.js-loading', size: 2, inline: true });
|
||||||
|
|
||||||
|
const statusBig = document.querySelector('.js-status-big');
|
||||||
|
const statusSmall = document.querySelector('.js-status-small');
|
||||||
|
const setStatusSmall = body => {
|
||||||
|
$removeChildren(statusSmall);
|
||||||
|
$appendChild(statusSmall, body);
|
||||||
|
};
|
||||||
|
|
||||||
|
setStatusSmall(<>Resolving Webfinger resource for @{FEDI_USERNAME}@{FEDI_INSTANCE}...</>);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { body } = await $xhr.get(`${location.protocol}//${FEDI_INSTANCE}/.well-known/webfinger?format=json&resource=acct:${FEDI_USERNAME}@${FEDI_INSTANCE}`, { type: 'json' });
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if(typeof body === 'object') {
|
||||||
|
if(Array.isArray(body.links))
|
||||||
|
for(const link of body.links)
|
||||||
|
if(typeof link === 'object' && link.rel === 'http://webfinger.net/rel/profile-page' && typeof link.href === 'string') {
|
||||||
|
url = link.href;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof url !== 'string' && Array.isArray(body.aliases))
|
||||||
|
for(const alias of body.aliases)
|
||||||
|
if(typeof alias === 'string') {
|
||||||
|
url = alias;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof url !== 'string' || (!url.startsWith('https://') && !url.startsWith('http://')))
|
||||||
|
throw new Error('Unable to resolve profile URL for given handle.');
|
||||||
|
|
||||||
|
setStatusSmall(<>Redirecting to <a href={url} rel="noopener">{url}</a>...</>);
|
||||||
|
location.replace(url);
|
||||||
|
} catch(ex) {
|
||||||
|
await loading.icon.stop();
|
||||||
|
loading.icon.batsu();
|
||||||
|
|
||||||
|
let errorText = ex.toString();
|
||||||
|
if(errorText.startsWith('['))
|
||||||
|
errorText = 'Something went wrong.';
|
||||||
|
|
||||||
|
statusBig.textContent = 'Profile not found!';
|
||||||
|
setStatusSmall(<span style="color: red">{errorText}</span>);
|
||||||
|
}
|
||||||
|
};
|
9
assets/redirects.js/main.js
Normal file
9
assets/redirects.js/main.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include bsky.jsx
|
||||||
|
#include fedi.jsx
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
if(location.pathname === '/bsky' || location.pathname.startsWith('/bsky/'))
|
||||||
|
MszRedirectsBsky();
|
||||||
|
if(location.pathname === '/fedi' || location.pathname.startsWith('/fedi/'))
|
||||||
|
MszRedirectsFedi();
|
||||||
|
})();
|
4
build.js
4
build.js
|
@ -25,14 +25,14 @@ const fs = require('fs');
|
||||||
{ source: 'common.js', target: '/assets', name: 'common.{hash}.js', },
|
{ source: 'common.js', target: '/assets', name: 'common.{hash}.js', },
|
||||||
{ source: 'misuzu.js', target: '/assets', name: 'misuzu.{hash}.js', },
|
{ source: 'misuzu.js', target: '/assets', name: 'misuzu.{hash}.js', },
|
||||||
{ source: 'oauth2.js', target: '/assets', name: 'oauth2.{hash}.js', },
|
{ source: 'oauth2.js', target: '/assets', name: 'oauth2.{hash}.js', },
|
||||||
{ source: 'redir-bsky.js', target: '/assets', name: 'redir-bsky.{hash}.js', },
|
{ source: 'redirects.js', target: '/assets', name: 'redirects.{hash}.js', },
|
||||||
{ source: 'redir-fedi.js', target: '/assets', name: 'redir-fedi.{hash}.js', },
|
|
||||||
],
|
],
|
||||||
css: [
|
css: [
|
||||||
{ source: 'errors.css', target: '/', name: 'errors.css', },
|
{ source: 'errors.css', target: '/', name: 'errors.css', },
|
||||||
{ source: 'common.css', target: '/assets', name: 'common.{hash}.css', },
|
{ source: 'common.css', target: '/assets', name: 'common.{hash}.css', },
|
||||||
{ source: 'misuzu.css', target: '/assets', name: 'misuzu.{hash}.css', },
|
{ source: 'misuzu.css', target: '/assets', name: 'misuzu.{hash}.css', },
|
||||||
{ source: 'oauth2.css', target: '/assets', name: 'oauth2.{hash}.css', },
|
{ source: 'oauth2.css', target: '/assets', name: 'oauth2.{hash}.css', },
|
||||||
|
{ source: 'redirects.css', target: '/assets', name: 'redirects.{hash}.css', },
|
||||||
],
|
],
|
||||||
twig: [
|
twig: [
|
||||||
{ source: 'errors/400', target: '/', name: 'error-400.html', },
|
{ source: 'errors/400', target: '/', name: 'error-400.html', },
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Redirects;
|
namespace Misuzu\Redirects;
|
||||||
|
|
||||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||||
|
use Misuzu\Template;
|
||||||
|
|
||||||
class LandingRedirectsRoutes implements RouteHandler {
|
class LandingRedirectsRoutes implements RouteHandler {
|
||||||
use RouteHandlerCommon;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
#[HttpGet('/')]
|
#[HttpGet('/')]
|
||||||
public function getIndex(): string {
|
public function getIndex(): string {
|
||||||
return <<<HTML
|
return Template::renderRaw('redirects.landing');
|
||||||
<!doctype html>
|
|
||||||
<meta charset=utf-8>
|
|
||||||
<title>Redirects</title>
|
|
||||||
<p>Short URL Service for <a href=/home rel=noopener>Flashii</a></p>
|
|
||||||
<p>This is a temporary landing page until backend is less tied up.</p>
|
|
||||||
<p><a href=https://flash.moe target=_blank rel=noopener>flashwave</a> 2017-2025</p>
|
|
||||||
HTML;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ class RedirectsContext {
|
||||||
public private(set) NamedRedirectsData $named;
|
public private(set) NamedRedirectsData $named;
|
||||||
public private(set) IncrementalRedirectsData $incremental;
|
public private(set) IncrementalRedirectsData $incremental;
|
||||||
|
|
||||||
|
public string $bskyProfileUrlFormat {
|
||||||
|
get => $this->config->getString('bsky_profile', 'https://bsky.app/profile/%s');
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DbConnection $dbConn,
|
DbConnection $dbConn,
|
||||||
public private(set) Config $config
|
public private(set) Config $config
|
||||||
|
|
|
@ -1,22 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Misuzu\Redirects;
|
namespace Misuzu\Redirects;
|
||||||
|
|
||||||
use Index\Config\Config;
|
|
||||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||||
use Misuzu\AssetInfo;
|
use Misuzu\Template;
|
||||||
|
|
||||||
class SocialRedirectsRoutes implements RouteHandler {
|
class SocialRedirectsRoutes implements RouteHandler {
|
||||||
use RouteHandlerCommon;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
private Config $config;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
RedirectsContext $redirectsCtx,
|
private RedirectsContext $redirectsCtx
|
||||||
private AssetInfo $assetInfo
|
) {}
|
||||||
) {
|
|
||||||
$this->config = $redirectsCtx->config->scopeTo('social');
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/bsky/((did:[a-z0-9]+:[A-Za-z0-9.\-_:%]+)|(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])))')]
|
#[HttpGet('/bsky/((did:[a-z0-9]+:[A-Za-z0-9.\-_:%]+)|(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])))')]
|
||||||
public function getBlueskyRedirect(HttpResponseBuilder $response, HttpRequest $request, string $handle): int|string {
|
public function getBlueskyRedirect(HttpResponseBuilder $response, HttpRequest $request, string $handle): int|string {
|
||||||
|
@ -41,38 +35,23 @@ class SocialRedirectsRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$format = $this->config->getString('bsky_profile', 'https://bsky.app/profile/%s');
|
$format = $this->redirectsCtx->bskyProfileUrlFormat;
|
||||||
if(is_string($did)) {
|
if(is_string($did)) {
|
||||||
$response->redirect(sprintf($format, $did), true);
|
$response->redirect(sprintf($format, $did), true);
|
||||||
return 301;
|
return 301;
|
||||||
}
|
}
|
||||||
|
|
||||||
$handle = rawurlencode($handle);
|
return Template::renderRaw('redirects.bsky', [
|
||||||
$script = $this->assetInfo->getAssetUrl('redir-bsky.js');
|
'bsky_format' => $format,
|
||||||
|
'bsky_handle' => $handle,
|
||||||
return <<<HTML
|
]);
|
||||||
<!doctype html>
|
|
||||||
<meta charset=utf-8>
|
|
||||||
<title>Redirecting to Bluesky profile...</title>
|
|
||||||
<div class=js-status><noscript>Javascript must be enabled for Bluesky redirects to work.</noscript></div>
|
|
||||||
<script>const BSKY_FORMAT = '{$format}'; const BSKY_HANDLE = decodeURIComponent('{$handle}');</script>
|
|
||||||
<script src="{$script}"></script>
|
|
||||||
HTML;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/fedi/([A-Za-z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})')]
|
#[HttpGet('/fedi/([A-Za-z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})')]
|
||||||
public function getFediverseRedirect(HttpResponseBuilder $response, HttpRequest $request, string $userName, string $instance): string {
|
public function getFediverseRedirect(HttpResponseBuilder $response, HttpRequest $request, string $userName, string $instance): string {
|
||||||
$userName = rawurlencode($userName);
|
return Template::renderRaw('redirects.fedi', [
|
||||||
$instance = rawurlencode($instance);
|
'fedi_username' => $userName,
|
||||||
$script = $this->assetInfo->getAssetUrl('redir-fedi.js');
|
'fedi_instance' => $instance,
|
||||||
|
]);
|
||||||
return <<<HTML
|
|
||||||
<!doctype html>
|
|
||||||
<meta charset=utf-8>
|
|
||||||
<title>Redirecting to Fediverse profile...</title>
|
|
||||||
<div class=js-status><noscript>Javascript must be enabled for Fediverse redirects to work.</noscript></div>
|
|
||||||
<script>const FEDI_USERNAME = '{$userName}'; const FEDI_INSTANCE = decodeURIComponent('{$instance}');</script>
|
|
||||||
<script src="{$script}"></script>
|
|
||||||
HTML;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
{% block html_head %}
|
{% block html_head %}
|
||||||
<meta name="description" content="{{ error_blerb }}">
|
<meta name="description" content="{{ error_blerb }}">
|
||||||
<link href="/vendor/fontawesome/css/all.min.css" type="text/css" rel="stylesheet">
|
<link href="/vendor/fontawesome/css/all.min.css" rel="stylesheet">
|
||||||
<link href="/errors.css" rel="stylesheet">
|
<link href="/errors.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if globals.eeprom_path is not empty and globals.eeprom_app is not empty %}
|
{% if globals.eeprom_path is not empty and globals.eeprom_app is not empty %}
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
const peepPath = '{{ globals.eeprom_path }}', peepApp = '{{ globals.eeprom_app }}';
|
const peepPath = '{{ globals.eeprom_path }}', peepApp = '{{ globals.eeprom_app }}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<button type="button" id="-msz-manage-setting-array-enter" class="input__button input__button--save">Add</button>
|
<button type="button" id="-msz-manage-setting-array-enter" class="input__button input__button--save">Add</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
var mszMSF = document.getElementById('-msz-manage-setting-form'),
|
var mszMSF = document.getElementById('-msz-manage-setting-form'),
|
||||||
mszMSA = document.getElementById('-msz-manage-setting-array'),
|
mszMSA = document.getElementById('-msz-manage-setting-array'),
|
||||||
mszMSAD = document.getElementById('-msz-manage-setting-array-delete'),
|
mszMSAD = document.getElementById('-msz-manage-setting-array-delete'),
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
{% block html_head %}
|
{% block html_head %}
|
||||||
{% include '_layout/meta.twig' %}
|
{% include '_layout/meta.twig' %}
|
||||||
<link href="/vendor/fontawesome/css/all.min.css" type="text/css" rel="stylesheet">
|
<link href="/vendor/fontawesome/css/all.min.css" rel="stylesheet">
|
||||||
<link href="{{ asset('common.css') }}" type="text/css" rel="stylesheet">
|
<link href="{{ asset('common.css') }}" rel="stylesheet">
|
||||||
<link href="{{ asset('misuzu.css') }}" type="text/css" rel="stylesheet">
|
<link href="{{ asset('misuzu.css') }}" rel="stylesheet">
|
||||||
{% if main_css_vars is defined and main_css_vars is iterable and main_css_vars is not empty %}
|
{% if main_css_vars is defined and main_css_vars is iterable and main_css_vars is not empty %}
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
{% include '_layout/footer.twig' %}
|
{% include '_layout/footer.twig' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<script src="/vendor/highlightjs/highlight.min.js" type="text/javascript"></script>
|
<script src="/vendor/highlightjs/highlight.min.js"></script>
|
||||||
<script src="{{ asset('common.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('common.js') }}"></script>
|
||||||
<script src="{{ asset('misuzu.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('misuzu.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% if globals.eeprom_path is not empty and globals.eeprom_app_messages is not empty %}
|
{% if globals.eeprom_path is not empty and globals.eeprom_app_messages is not empty %}
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
const peepPath = '{{ globals.eeprom_path }}', peepApp = '{{ globals.eeprom_app_messages }}';
|
const peepPath = '{{ globals.eeprom_path }}', peepApp = '{{ globals.eeprom_app_messages }}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
19
templates/redirects/bsky.twig
Normal file
19
templates/redirects/bsky.twig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends 'redirects/master.twig' %}
|
||||||
|
|
||||||
|
{% set html_title = 'Redirecting to Bluesky profile...' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="redir-social">
|
||||||
|
<div class="redir-social-content">
|
||||||
|
<div class="js-loading"></div>
|
||||||
|
<article class="redir-social-body">
|
||||||
|
<h1 class="js-status-big">Redirecting to profile...</h1>
|
||||||
|
<p class="js-status-small"><noscript>Javascript must be enabled for Bluesky redirects to work.</noscript></p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const BSKY_FORMAT = '{{ bsky_format }}';
|
||||||
|
const BSKY_HANDLE = '{{ bsky_handle }}';
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
19
templates/redirects/fedi.twig
Normal file
19
templates/redirects/fedi.twig
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends 'redirects/master.twig' %}
|
||||||
|
|
||||||
|
{% set html_title = 'Redirecting to Fediverse profile...' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="redir-social">
|
||||||
|
<div class="redir-social-content">
|
||||||
|
<div class="js-loading"></div>
|
||||||
|
<article class="redir-social-body">
|
||||||
|
<h1 class="js-status-big">Redirecting to profile...</h1>
|
||||||
|
<p class="js-status-small"><noscript>Javascript must be enabled for Fediverse redirects to work.</noscript></p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const FEDI_USERNAME = '{{ fedi_username }}';
|
||||||
|
const FEDI_INSTANCE = '{{ fedi_instance }}';
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
29
templates/redirects/landing.twig
Normal file
29
templates/redirects/landing.twig
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{% extends 'redirects/master.twig' %}
|
||||||
|
|
||||||
|
{% set html_title = globals.site_info.name ~ ' Redirect Service' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="redir-landing">
|
||||||
|
<div class="redir-landing-content">
|
||||||
|
<article class="redir-landing-body">
|
||||||
|
<p><div class="redir-landing-logo"></div></p>
|
||||||
|
<h1>{{ html_title }}</h1>
|
||||||
|
<p>Short URL Service for <a href="{{ globals.site_info.url }}" rel="noopener">{{ globals.site_info.name }}</a></p>
|
||||||
|
</article>
|
||||||
|
<footer class="redir-landing-footer">
|
||||||
|
<p>
|
||||||
|
<a href="https://flash.moe" target="_blank" rel="noopener">flashwave</a>
|
||||||
|
2013-{{ ''|date('Y') }} /
|
||||||
|
{% set git_branch = git_branch() %}
|
||||||
|
{% if git_branch != 'HEAD' %}
|
||||||
|
<a href="https://patchii.net/flashii/misuzu/src/branch/{{ git_branch }}" target="_blank" rel="noopener">{{ git_branch }}</a>
|
||||||
|
{% else %}
|
||||||
|
{% set git_tag = git_tag() %}
|
||||||
|
<a href="https://patchii.net/flashii/misuzu/src/tag/{{ git_tag }}" target="_blank" rel="noopener">{{ git_tag }}</a>
|
||||||
|
{% endif %}
|
||||||
|
# <a href="https://patchii.net/flashii/misuzu/commit/{{ git_commit_hash(true) }}" target="_blank" rel="noopener">{{ git_commit_hash() }}</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
16
templates/redirects/master.twig
Normal file
16
templates/redirects/master.twig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends 'html.twig' %}
|
||||||
|
|
||||||
|
{% block html_head %}
|
||||||
|
<link href="/vendor/fontawesome/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="{{ asset('common.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ asset('redirects.css') }}" rel="stylesheet">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block html_body %}
|
||||||
|
<div class="redir-background"></div>
|
||||||
|
<div class="redir-foreground">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<script src="{{ asset('common.js') }}"></script>
|
||||||
|
<script src="{{ asset('redirects.js') }}"></script>
|
||||||
|
{% endblock %}
|
Loading…
Add table
Reference in a new issue