(add) aid utility

This commit is contained in:
kakkokari-gtyih 2023-10-28 23:24:50 +09:00
parent db7af6bcdd
commit bcfa816d0d
7 changed files with 307 additions and 3 deletions

43
assets/js/mi/aid.ts Normal file
View File

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
// AID
// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列]
import * as crypto from 'crypto';
export const aidRegExp = /^[0-9a-z]{10}$/;
const TIME2000 = 946684800000;
let counter: number;
if (process.client) {
const arr = window.crypto.getRandomValues(new Uint16Array(2));
counter = parseInt(arr[0].toString());
} else {
counter = crypto.randomBytes(2).readUInt16LE(0);
}
function getTime(time: number): string {
time = time - TIME2000;
if (time < 0) time = 0;
return time.toString(36).padStart(8, '0');
}
function getNoise(): string {
return counter.toString(36).padStart(2, '0').slice(-2);
}
export function genAid(t: number): string {
if (isNaN(t)) throw new Error('Failed to create AID: Invalid Date');
counter++;
return getTime(t) + getNoise();
}
export function parseAid(id: string): { date: Date; } {
const time = parseInt(id.slice(0, 8), 36) + TIME2000;
return { date: new Date(time) };
}

43
assets/js/mi/aidx.ts Normal file
View File

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
// AIDX
// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ4の[個体ID] + 長さ4の[カウンタ]
// (c) mei23
// https://misskey.m544.net/notes/71899acdcc9859ec5708ac24
import { customAlphabet } from 'nanoid';
export const aidxRegExp = /^[0-9a-z]{16}$/;
const TIME2000 = 946684800000;
const TIME_LENGTH = 8;
const NODE_LENGTH = 4;
const NOISE_LENGTH = 4;
const nodeId = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', NODE_LENGTH)();
let counter = 0;
function getTime(time: number): string {
time = time - TIME2000;
if (time < 0) time = 0;
return time.toString(36).padStart(TIME_LENGTH, '0').slice(-TIME_LENGTH);
}
function getNoise(): string {
return counter.toString(36).padStart(NOISE_LENGTH, '0').slice(-NOISE_LENGTH);
}
export function genAidx(t: number): string {
if (isNaN(t)) throw new Error('Failed to create AIDX: Invalid Date');
counter++;
return getTime(t) + nodeId + getNoise();
}
export function parseAidx(id: string): { date: Date; } {
const time = parseInt(id.slice(0, TIME_LENGTH), 36) + TIME2000;
return { date: new Date(time) };
}

View File

@ -191,6 +191,13 @@ _mfmPlayground:
clearEmojiCacheDescription: "絵文字が表示されないとき" clearEmojiCacheDescription: "絵文字が表示されないとき"
clearEmojiCache: "絵文字のキャッシュを削除" clearEmojiCache: "絵文字のキャッシュを削除"
_aidConverter:
title: "aid/aidxツール"
aidToDate: "aid/aidx→日付時刻"
dateToAid: "日付時刻→aid/aidx"
mode: "作成するid"
date: "日付時刻"
_api: _api:
_permissions: _permissions:
title: "権限" title: "権限"

View File

@ -40,6 +40,7 @@
}, },
"packageManager": "pnpm@8.6.0", "packageManager": "pnpm@8.6.0",
"dependencies": { "dependencies": {
"js-yaml": "^4.1.0" "js-yaml": "^4.1.0",
"nanoid": "^5.0.2"
} }
} }

View File

@ -0,0 +1,79 @@
<template>
<div class='container mx-auto max-w-screen-xl px-6 py-6'>
<h1 class='text-2xl lg:text-3xl font-bold mb-4'>
{{ $t('_aidConverter.title') }}
</h1>
<div class="grid grid-cols-2 gap-8 mx-auto max-w-lg mb-4">
<button :class="['rounded-full py-2 hover:bg-accent-600/60', {'bg-accent-600/40': (tab === 'aidToDate')}]" @click="tab = 'aidToDate'">{{ $t('_aidConverter.aidToDate') }}</button>
<button :class="['rounded-full py-2 hover:bg-accent-600/60', {'bg-accent-600/40': (tab === 'dateToAid')}]" @click="tab = 'dateToAid'">{{ $t('_aidConverter.aidToDate') }}</button>
</div>
<div v-if="tab === 'aidToDate'" class="mx-auto max-w-lg">
<label class="mb-1" for="aidToDateAid">aid / aidx</label>
<input class="form-control" id="aidToDateAid" v-model="aidToDateAid" />
<div class="my-2">
<button class="btn btn-primary" @click="doAidToDate()">{{ $t('_aidConverter.aidToDate') }}</button>
</div>
<div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600">
{{ aidToDateResult }}
</div>
</div>
<div v-if="tab === 'dateToAid'" class="mx-auto max-w-lg">
<label class="mb-1" for="dateToAidDate">{{ $t('_aidConverter.date') }}</label>
<input class="form-control mb-2" id="dateToAidDate" type="datetime-local" v-model="dateToAidDate" />
<label class="mb-1" for="DateToAidMode">{{ $t('_aidConverter.mode') }}</label>
<select class="form-select" id="DateToAidMode" v-model="dateToAidMode">
<option value="aid">aid</option>
<option value="aidx">aidx</option>
</select>
<div class="my-2">
<button class="btn btn-primary" @click="doDateToAid()">{{ $t('_aidConverter.dateToAid') }}</button>
</div>
<div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600">
{{ dateToAidResult }}
</div>
</div>
</div>
</template>
<script setup lang='ts'>
import { genAid } from '~/assets/js/mi/aid';
import { genAidx } from '~/assets/js/mi/aidx';
definePageMeta({
layout: 'tools',
});
const TIME2000 = 946684800000;
const tab = ref<'aidToDate' | 'dateToAid'>('aidToDate');
const aidToDateAid = ref<string>('');
const aidToDateResult = ref<string>('');
function doAidToDate() {
const d = new Date(parseInt(aidToDateAid.value.slice(0, 8), 36) + TIME2000);
aidToDateResult.value = d.toLocaleString();
}
const dateToAidDate = ref<string>('');
const dateToAidMode = ref<'aid' | 'aidx'>('aid');
const dateToAidResult = ref<string>('');
function doDateToAid() {
const d = new Date(dateToAidDate.value);
if (dateToAidMode.value === 'aid') {
dateToAidResult.value = genAid(d.getTime());
} else if (dateToAidMode.value === 'aidx') {
dateToAidResult.value = genAidx(d.getTime());
}
}
</script>
<style module>
.mfmRoot {
@apply rounded-lg p-6 border break-words overflow-hidden;
font-family: Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;
line-height: 1.35;
}
.mfmRoot img {
display: inline;
}
</style>

View File

@ -8,6 +8,9 @@ dependencies:
js-yaml: js-yaml:
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0 version: 4.1.0
nanoid:
specifier: ^5.0.2
version: 5.0.2
devDependencies: devDependencies:
'@babel/parser': '@babel/parser':
@ -1166,6 +1169,33 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@nuxt/kit@3.8.0:
resolution: {integrity: sha512-oIthQxeMIVs4ESVP5FqLYn8tj0S1sLd+eYreh+dNYgnJ2pTi7+THR12ONBNHjk668jqEe7ErUJ8UlGwqBzgezg==}
engines: {node: ^14.18.0 || >=16.10.0}
dependencies:
'@nuxt/schema': 3.8.0
c12: 1.5.1
consola: 3.2.3
defu: 6.1.2
globby: 13.2.2
hash-sum: 2.0.0
ignore: 5.2.4
jiti: 1.20.0
knitwork: 1.0.0
mlly: 1.4.2
pathe: 1.1.1
pkg-types: 1.0.3
scule: 1.0.0
semver: 7.5.4
ufo: 1.3.1
unctx: 2.3.1
unimport: 3.4.0
untyped: 1.4.0
transitivePeerDependencies:
- rollup
- supports-color
dev: true
/@nuxt/schema@3.7.3: /@nuxt/schema@3.7.3:
resolution: {integrity: sha512-Uqe3Z9RnAROzv5owQo//PztD9d4csKK6ulwQO1hIAinCh34X7z2zrv9lhm14hlRYU1n7ISEi4S7UeHgL/r8d8A==} resolution: {integrity: sha512-Uqe3Z9RnAROzv5owQo//PztD9d4csKK6ulwQO1hIAinCh34X7z2zrv9lhm14hlRYU1n7ISEi4S7UeHgL/r8d8A==}
engines: {node: ^14.18.0 || >=16.10.0} engines: {node: ^14.18.0 || >=16.10.0}
@ -1185,6 +1215,26 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@nuxt/schema@3.8.0:
resolution: {integrity: sha512-VEDVeCjdVowhoY5vIBSz94+SSwmM204jN6TNe/ShBJ2d/vZiy9EtLbhOwqaPNFHwnN1fl/XFHThwJiexdB9D1w==}
engines: {node: ^14.18.0 || >=16.10.0}
dependencies:
'@nuxt/ui-templates': 1.3.1
consola: 3.2.3
defu: 6.1.2
hookable: 5.5.3
pathe: 1.1.1
pkg-types: 1.0.3
postcss-import-resolver: 2.0.0
std-env: 3.4.3
ufo: 1.3.1
unimport: 3.4.0
untyped: 1.4.0
transitivePeerDependencies:
- rollup
- supports-color
dev: true
/@nuxt/telemetry@2.5.0: /@nuxt/telemetry@2.5.0:
resolution: {integrity: sha512-7vZyOHfCAZg1PuCwy3B87MQOezW4pf8BC3gNDL92FW24BuLF0dl/BbFfxPeRxvjivuj5kkNM78x/qzNRCKfZgw==} resolution: {integrity: sha512-7vZyOHfCAZg1PuCwy3B87MQOezW4pf8BC3gNDL92FW24BuLF0dl/BbFfxPeRxvjivuj5kkNM78x/qzNRCKfZgw==}
hasBin: true hasBin: true
@ -1341,7 +1391,7 @@ packages:
/@nuxtjs/mdc@0.1.6: /@nuxtjs/mdc@0.1.6:
resolution: {integrity: sha512-zJuq5KwU3d1Dlh1sudnpVtIFoap09ZrvO9IAM1iP4tipzSRkgHFbCOTMEmK17Rx7KSdmvBbFP+/4MBaJdj1NqQ==} resolution: {integrity: sha512-zJuq5KwU3d1Dlh1sudnpVtIFoap09ZrvO9IAM1iP4tipzSRkgHFbCOTMEmK17Rx7KSdmvBbFP+/4MBaJdj1NqQ==}
dependencies: dependencies:
'@nuxt/kit': 3.7.3 '@nuxt/kit': 3.8.0
'@types/hast': 3.0.1 '@types/hast': 3.0.1
'@types/mdast': 4.0.0 '@types/mdast': 4.0.0
'@vue/compiler-core': 3.3.4 '@vue/compiler-core': 3.3.4
@ -2232,6 +2282,15 @@ packages:
- supports-color - supports-color
dev: true dev: true
/agent-base@7.1.0:
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
engines: {node: '>= 14'}
dependencies:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/ansi-colors@4.1.3: /ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2499,6 +2558,24 @@ packages:
- supports-color - supports-color
dev: true dev: true
/c12@1.5.1:
resolution: {integrity: sha512-BWZRJgDEveT8uI+cliCwvYSSSSvb4xKoiiu5S0jaDbKBopQLQF7E+bq9xKk1pTcG+mUa3yXuFO7bD9d8Lr9Xxg==}
dependencies:
chokidar: 3.5.3
defu: 6.1.2
dotenv: 16.3.1
giget: 1.1.3
jiti: 1.20.0
mlly: 1.4.2
ohash: 1.1.3
pathe: 1.1.1
perfect-debounce: 1.0.0
pkg-types: 1.0.3
rc9: 2.1.1
transitivePeerDependencies:
- supports-color
dev: true
/cac@6.7.14: /cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -3583,6 +3660,21 @@ packages:
- supports-color - supports-color
dev: true dev: true
/giget@1.1.3:
resolution: {integrity: sha512-zHuCeqtfgqgDwvXlR84UNgnJDuUHQcNI5OqWqFxxuk2BshuKbYhJWdxBsEo4PvKqoGh23lUAIvBNpChMLv7/9Q==}
hasBin: true
dependencies:
colorette: 2.0.20
defu: 6.1.2
https-proxy-agent: 7.0.2
mri: 1.2.0
node-fetch-native: 1.4.0
pathe: 1.1.1
tar: 6.2.0
transitivePeerDependencies:
- supports-color
dev: true
/git-config-path@2.0.0: /git-config-path@2.0.0:
resolution: {integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==} resolution: {integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -3879,6 +3971,16 @@ packages:
- supports-color - supports-color
dev: true dev: true
/https-proxy-agent@7.0.2:
resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/httpxy@0.1.5: /httpxy@0.1.5:
resolution: {integrity: sha512-hqLDO+rfststuyEUTWObQK6zHEEmZ/kaIP2/zclGGZn6X8h/ESTWg+WKecQ/e5k4nPswjzZD+q2VqZIbr15CoQ==} resolution: {integrity: sha512-hqLDO+rfststuyEUTWObQK6zHEEmZ/kaIP2/zclGGZn6X8h/ESTWg+WKecQ/e5k4nPswjzZD+q2VqZIbr15CoQ==}
dev: true dev: true
@ -5153,6 +5255,12 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/nanoid@5.0.2:
resolution: {integrity: sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/napi-wasm@1.1.0: /napi-wasm@1.1.0:
resolution: {integrity: sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==} resolution: {integrity: sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg==}
dev: true dev: true
@ -6971,6 +7079,10 @@ packages:
resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==}
dev: true dev: true
/ufo@1.3.1:
resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==}
dev: true
/ultrahtml@1.5.2: /ultrahtml@1.5.2:
resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==} resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==}
dev: true dev: true
@ -7061,6 +7173,24 @@ packages:
- rollup - rollup
dev: true dev: true
/unimport@3.4.0:
resolution: {integrity: sha512-M/lfFEgufIT156QAr/jWHLUn55kEmxBBiQsMxvRSIbquwmeJEyQYgshHDEvQDWlSJrVOOTAgnJ3FvlsrpGkanA==}
dependencies:
'@rollup/pluginutils': 5.0.4(rollup@3.29.2)
escape-string-regexp: 5.0.0
fast-glob: 3.3.1
local-pkg: 0.4.3
magic-string: 0.30.3
mlly: 1.4.2
pathe: 1.1.1
pkg-types: 1.0.3
scule: 1.0.0
strip-literal: 1.3.0
unplugin: 1.5.0
transitivePeerDependencies:
- rollup
dev: true
/unist-builder@4.0.0: /unist-builder@4.0.0:
resolution: {integrity: sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==} resolution: {integrity: sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==}
dependencies: dependencies:

View File

@ -19,10 +19,11 @@ export default defineNitroPlugin((nitroApp) => {
remainingList.push(...(v.match(/<meta[^>]+>/gm) ?? [])); remainingList.push(...(v.match(/<meta[^>]+>/gm) ?? []));
remainingList.push(...(v.match(/<script type="application\/ld\+json">.*(?<=<\/script>)/gm) ?? [])); remainingList.push(...(v.match(/<script type="application\/ld\+json">.*(?<=<\/script>)/gm) ?? []));
}); });
remainingList.push('');
html.head = remainingList.map((v) => v + '\n'); html.head = remainingList.map((v) => v + '\n');
//@ts-ignore //@ts-ignore
html.head.push('<script type="text/javascript">const s = ' + JSON.stringify(runtimeConfig.public.locales.map((l) => l.code)) + '; const d = new URLSearchParams(document.cookie); if (d.get(\'i18n_redirected\')) { location.replace(\'/\' + d.get(\'i18n_redirected\') + location.pathname); } else if (s.includes(navigator.language.split("-")[0])) { location.replace(\'/\' + navigator.language.split("-")[0] + location.pathname); } else { location.replace(\'/ja\' + location.pathname); }</script>\n'); html.head.push('<script type="text/javascript">const s = ' + JSON.stringify(runtimeConfig.locales.map((l) => l.code)) + '; const d = new URLSearchParams(document.cookie); if (d.get(\'i18n_redirected\')) { location.replace(\'/\' + d.get(\'i18n_redirected\') + location.pathname); } else if (s.includes(navigator.language.split("-")[0])) { location.replace(\'/\' + navigator.language.split("-")[0] + location.pathname); } else { location.replace(\'/ja\' + location.pathname); }</script>\n');
html.body = ['\n<noscript>Please enable Javascript to see this page properly.</noscript>\n']; html.body = ['\n<noscript>Please enable Javascript to see this page properly.</noscript>\n'];
html.bodyAppend = []; html.bodyAppend = [];
html.bodyPrepend = []; html.bodyPrepend = [];