mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 06:03:34 +01:00
Added init
This commit is contained in:
parent
f7b1311e36
commit
56782fc2d3
8 changed files with 141 additions and 13 deletions
|
@ -11,7 +11,8 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-switch": "^1.1.0"
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"react-day-picker": "^9.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
|
|
|
@ -6,7 +6,7 @@ import {NavLink, Outlet, useNavigate} from "react-router-dom";
|
|||
import {useStore} from "./store/store.ts";
|
||||
import {LoadingScreen} from "./utils/LoadingScreen.tsx";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import {Cable, Construction, Crown, NotepadText, Wrench, PhoneCall} from "lucide-react";
|
||||
import {Cable, Construction, Crown, NotepadText, Wrench, PhoneCall,Paintbrush } from "lucide-react";
|
||||
|
||||
const WS_URL = import.meta.env.DEV? 'http://localhost:9001' : ''
|
||||
export const App = ()=> {
|
||||
|
@ -93,18 +93,19 @@ export const App = ()=> {
|
|||
<Crown width={40} height={40}/>
|
||||
<h1>Etherpad</h1>
|
||||
</span>
|
||||
<ul>
|
||||
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
||||
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
||||
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
||||
<li><NavLink to={"/pads"}><NotepadText/><Trans
|
||||
i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li>
|
||||
<li><NavLink to={"/shout"}><PhoneCall/>Communication</NavLink></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
||||
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
||||
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
||||
<li><NavLink to={"/pads"}><NotepadText/><Trans
|
||||
i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li>
|
||||
<li><NavLink to={"/shout"}><PhoneCall/>Communication</NavLink></li>
|
||||
<li><NavLink to={"/cleanup"}><Paintbrush/>Cleanup</NavLink></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="innerwrapper">
|
||||
<Outlet/>
|
||||
<div className="innerwrapper">
|
||||
<Outlet/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
22
admin/src/components/DatetimeInput.tsx
Normal file
22
admin/src/components/DatetimeInput.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { DayPicker } from "react-day-picker";
|
||||
import "react-day-picker/style.css";
|
||||
import {FC} from "react";
|
||||
|
||||
type DatetimeInputProps = {
|
||||
value: Date,
|
||||
onChange: (value: Date) => void
|
||||
}
|
||||
|
||||
export const DatetimeInput:FC<DatetimeInputProps> = ({
|
||||
onChange,value
|
||||
})=>{
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
required
|
||||
mode="single"
|
||||
selected={value}
|
||||
onSelect={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -13,6 +13,7 @@ import i18n from "./localization/i18n.ts";
|
|||
import {PadPage} from "./pages/PadPage.tsx";
|
||||
import {ToastDialog} from "./utils/Toast.tsx";
|
||||
import {ShoutPage} from "./pages/ShoutPage.tsx";
|
||||
import {AuthorCleanupScreen} from "./pages/AuthorCleanupScreen.tsx";
|
||||
|
||||
const router = createBrowserRouter(createRoutesFromElements(
|
||||
<><Route element={<App/>}>
|
||||
|
@ -22,6 +23,7 @@ const router = createBrowserRouter(createRoutesFromElements(
|
|||
<Route path="/help" element={<HelpPage/>}/>
|
||||
<Route path="/pads" element={<PadPage/>}/>
|
||||
<Route path="/shout" element={<ShoutPage/>}/>
|
||||
<Route path="/cleanup" element={<AuthorCleanupScreen/>}/>
|
||||
</Route><Route path="/login">
|
||||
<Route index element={<LoginScreen/>}/>
|
||||
</Route></>
|
||||
|
|
52
admin/src/pages/AuthorCleanupScreen.tsx
Normal file
52
admin/src/pages/AuthorCleanupScreen.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import {useState} from "react";
|
||||
import {DatetimeInput} from "../components/DatetimeInput.tsx";
|
||||
import {IconButton} from "../components/IconButton.tsx";
|
||||
import {PaintRoller} from "lucide-react";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import {useStore} from "../store/store.ts";
|
||||
|
||||
export const AuthorCleanupScreen = ()=>{
|
||||
const [cleanUpBefore, setCleanUpBefore] = useState<Date>()
|
||||
const [openDialog, setOpenDialog] = useState<boolean>(false)
|
||||
const settingsSocket = useStore(state=>state.settingsSocket)
|
||||
|
||||
|
||||
const deleteAuthorsBefore = (date: Date)=>{
|
||||
settingsSocket?.emit('deleteAuthorsBefore', date.getTime())
|
||||
}
|
||||
|
||||
return <div>
|
||||
<Dialog.Root open={openDialog}><Dialog.Portal>
|
||||
<Dialog.Overlay className="dialog-confirm-overlay" />
|
||||
<Dialog.Content className="dialog-confirm-content">
|
||||
<div className="">
|
||||
<div className=""></div>
|
||||
<div className="">
|
||||
Delete all authors before {cleanUpBefore?.toLocaleString()}?
|
||||
</div>
|
||||
<div className="settings-button-bar">
|
||||
<button onClick={()=>{
|
||||
setOpenDialog(false)
|
||||
}}>Cancel</button>
|
||||
<button onClick={()=>{
|
||||
deleteAuthorsBefore(cleanUpBefore!)
|
||||
setOpenDialog(false)
|
||||
}}>Ok</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<h1>Author cleanup</h1>
|
||||
|
||||
<div>
|
||||
<DatetimeInput onChange={(c)=>setCleanUpBefore(c)} value={cleanUpBefore!}/>
|
||||
|
||||
{cleanUpBefore&&<p>All authors before {cleanUpBefore.toLocaleString()} will be deleted</p>}
|
||||
|
||||
<IconButton disabled={cleanUpBefore == undefined} icon={<PaintRoller/>} title="Delete authors?" onClick={()=>{
|
||||
setOpenDialog(true)
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
|
@ -27,6 +27,9 @@ importers:
|
|||
'@radix-ui/react-switch':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-day-picker:
|
||||
specifier: ^9.0.7
|
||||
version: 9.0.7(react@18.3.1)
|
||||
devDependencies:
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.1
|
||||
|
@ -2185,6 +2188,9 @@ packages:
|
|||
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
date-fns@3.6.0:
|
||||
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
||||
|
||||
date-format@4.0.14:
|
||||
resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
@ -3712,6 +3718,11 @@ packages:
|
|||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
react-day-picker@9.0.7:
|
||||
resolution: {integrity: sha512-JTa9vhhfxWSVfhzTFG5rEXynsDb6zi3NG+0EHbs5JBZuGFlNbg8qyagh9YtIR1ccGThwY339CAqFOH6op55omw==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
|
||||
react-dom@18.3.1:
|
||||
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
|
||||
peerDependencies:
|
||||
|
@ -6333,6 +6344,8 @@ snapshots:
|
|||
es-errors: 1.3.0
|
||||
is-data-view: 1.0.1
|
||||
|
||||
date-fns@3.6.0: {}
|
||||
|
||||
date-format@4.0.14: {}
|
||||
|
||||
debug@2.6.9:
|
||||
|
@ -8157,6 +8170,11 @@ snapshots:
|
|||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
|
||||
react-day-picker@9.0.7(react@18.3.1):
|
||||
dependencies:
|
||||
date-fns: 3.6.0
|
||||
react: 18.3.1
|
||||
|
||||
react-dom@18.3.1(react@18.3.1):
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
|
|
@ -297,6 +297,11 @@ exports.addPad = async (authorID: string, padID: string) => {
|
|||
await db.set(`globalAuthor:${authorID}`, author);
|
||||
};
|
||||
|
||||
|
||||
exports.listAllAuthorsKeys = async () => {
|
||||
return await db.findKeys('globalAuthor:*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a pad from the list of contributions
|
||||
* @param {String} authorID The id of the author
|
||||
|
|
|
@ -13,7 +13,7 @@ const settings = require('../../utils/Settings');
|
|||
const UpdateCheck = require('../../utils/UpdateCheck');
|
||||
const padManager = require('../../db/PadManager');
|
||||
const api = require('../../db/API');
|
||||
|
||||
const authorManager = require('../../db/AuthorManager');
|
||||
|
||||
const queryPadLimit = 12;
|
||||
const logger = log4js.getLogger('adminSettings');
|
||||
|
@ -252,6 +252,33 @@ exports.socketio = (hookName: string, {io}: any) => {
|
|||
}
|
||||
})
|
||||
|
||||
socket.on('deleteAuthorsBefore', async (time: number) => {
|
||||
const allGlobalAuthors = await authorManager.listAllAuthorsKeys()
|
||||
const authorId = authorManager.createAuthor("inactive_user")
|
||||
const {padIDs} = await padManager.listAllPads()
|
||||
|
||||
for (const authorKey of allGlobalAuthors) {
|
||||
const authorId = authorKey.split(":")[1]
|
||||
console.log("global Authors are",allGlobalAuthors)
|
||||
const author = await authorManager.getAuthor(authorId)
|
||||
console.log("Author is", author)
|
||||
if (author.timestamp < time) {
|
||||
if ("padIDs" in author) {
|
||||
for (const padId of Object.keys(author.padIDs)) {
|
||||
const pad = padManager.getPad(padId) as PadType
|
||||
console.log("Pad is", pad)
|
||||
const headRevision = pad.head
|
||||
for (let i = 0; i < headRevision; i++) {
|
||||
const rev = await pad.getRevision(i)
|
||||
console.log("Revision is", rev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
socket.on('restartServer', async () => {
|
||||
logger.info('Admin request to restart server through a socket on /admin/settings');
|
||||
settings.reloadSettings();
|
||||
|
|
Loading…
Reference in a new issue