@meith/web 0.26.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/admin/system/page.tsx +29 -1
- package/app/admin/themes/page.tsx +2 -2
- package/package.json +48 -48
- package/src/components/admin/forum-forms.tsx +23 -8
- package/src/components/admin/system-forms.tsx +22 -0
- package/src/components/content/markdown-editor.tsx +4 -1
- package/src/server/system-admin-actions.ts +24 -1
- package/src/server/upgrade-notice.ts +85 -3
- package/src/view/admin-copy.ts +1 -0
- package/src/view/admin-panel-copy.ts +2 -0
- package/src/view/setting-groups.ts +0 -2
|
@@ -4,6 +4,7 @@ import type { TaskHealthStatus } from '@meith/tasks'
|
|
|
4
4
|
import { cn } from '@meith/ui'
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
+
ApplyMigrationsForm,
|
|
7
8
|
ClearCacheForm,
|
|
8
9
|
PruneSessionsForm,
|
|
9
10
|
PruneTokensForm,
|
|
@@ -16,6 +17,7 @@ import { PanelPage } from '@/components/shell/panel-page'
|
|
|
16
17
|
import { adminPageContext } from '@/server/admin'
|
|
17
18
|
import { getTranslator, tr } from '@/server/i18n'
|
|
18
19
|
import { buildSystemHealthView } from '@/server/system-admin'
|
|
20
|
+
import { CODE_VERSION, pendingUpgradeNotice } from '@/server/upgrade-notice'
|
|
19
21
|
import { systemFormsCopy } from '@/view/admin-panel-copy'
|
|
20
22
|
import { formatTime } from '@/view/time'
|
|
21
23
|
|
|
@@ -36,7 +38,11 @@ export default async function AdminSystemPage() {
|
|
|
36
38
|
if ((await adminPageContext()) === null) return null
|
|
37
39
|
|
|
38
40
|
const now = new Date()
|
|
39
|
-
const [translator, view] = await Promise.all([
|
|
41
|
+
const [translator, view, upgradeNotice] = await Promise.all([
|
|
42
|
+
getTranslator(),
|
|
43
|
+
buildSystemHealthView(now),
|
|
44
|
+
pendingUpgradeNotice(),
|
|
45
|
+
])
|
|
40
46
|
const copy = systemFormsCopy(translator)
|
|
41
47
|
|
|
42
48
|
if (view === null) {
|
|
@@ -246,6 +252,28 @@ export default async function AdminSystemPage() {
|
|
|
246
252
|
</ul>
|
|
247
253
|
</section>
|
|
248
254
|
|
|
255
|
+
<section className={PANEL_CARD}>
|
|
256
|
+
<h2 className="font-heading text-lg font-semibold">
|
|
257
|
+
{translator.t('adminSystem.migrations')}
|
|
258
|
+
</h2>
|
|
259
|
+
<p className="text-sm">
|
|
260
|
+
{translator.t('adminSystem.runningVersion')} <code>{CODE_VERSION}</code>.
|
|
261
|
+
</p>
|
|
262
|
+
{upgradeNotice === null ? (
|
|
263
|
+
<p className="text-sm text-muted-foreground">
|
|
264
|
+
{translator.t('adminSystem.migrationsUpToDate')}
|
|
265
|
+
</p>
|
|
266
|
+
) : (
|
|
267
|
+
<>
|
|
268
|
+
<p className="text-sm text-destructive">{upgradeNotice}</p>
|
|
269
|
+
<p className="text-sm text-muted-foreground">
|
|
270
|
+
{translator.t('adminSystem.migrationsHint')}
|
|
271
|
+
</p>
|
|
272
|
+
<ApplyMigrationsForm copy={copy} />
|
|
273
|
+
</>
|
|
274
|
+
)}
|
|
275
|
+
</section>
|
|
276
|
+
|
|
249
277
|
<section className={PANEL_CARD}>
|
|
250
278
|
<h2 className="font-heading text-lg font-semibold">
|
|
251
279
|
{translator.t('adminSystem.recount')}
|
|
@@ -167,8 +167,8 @@ export default async function AdminThemesPage() {
|
|
|
167
167
|
</p>
|
|
168
168
|
<p className="text-muted-foreground">
|
|
169
169
|
{translator.t('adminThemes.deployBefore')} <em>exist</em>
|
|
170
|
-
{translator.t('adminThemes.deployAfterExist')}
|
|
171
|
-
{translator.t('adminThemes.deployAfterAdd')}{' '}
|
|
170
|
+
{translator.t('adminThemes.deployAfterExist')}{' '}
|
|
171
|
+
<code className="text-xs">npm install</code> {translator.t('adminThemes.deployAfterAdd')}{' '}
|
|
172
172
|
<code className="text-xs">meith.config.ts</code>
|
|
173
173
|
{translator.t('adminThemes.deployAfterConfig')}{' '}
|
|
174
174
|
<code className="text-xs">defaultTheme</code> {translator.t('adminThemes.deployEnd')}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meith/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "The board itself: the Next.js app, and the forum-web bin that materializes it into an external board workspace.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,53 +43,53 @@
|
|
|
43
43
|
"react-dom": "19.2.8",
|
|
44
44
|
"tailwindcss": "^4.3.3",
|
|
45
45
|
"typescript": "7.0.2",
|
|
46
|
-
"@meith/accounts": "0.
|
|
47
|
-
"@meith/admin": "0.
|
|
48
|
-
"@meith/antispam": "0.
|
|
49
|
-
"@meith/api": "0.
|
|
50
|
-
"@meith/attachments": "0.
|
|
51
|
-
"@meith/authorization": "0.
|
|
52
|
-
"@meith/avatars": "0.
|
|
53
|
-
"@meith/core": "0.
|
|
54
|
-
"@meith/db": "0.
|
|
55
|
-
"@meith/demo": "0.
|
|
56
|
-
"@meith/drafts": "0.
|
|
57
|
-
"@meith/drivers": "0.
|
|
58
|
-
"@meith/events": "0.
|
|
59
|
-
"@meith/forums": "0.
|
|
60
|
-
"@meith/
|
|
61
|
-
"@meith/
|
|
62
|
-
"@meith/import": "0.
|
|
63
|
-
"@meith/install": "0.
|
|
64
|
-
"@meith/mail": "0.
|
|
65
|
-
"@meith/markdown": "0.
|
|
66
|
-
"@meith/marketplace": "0.
|
|
67
|
-
"@meith/messages": "0.
|
|
68
|
-
"@meith/moderation": "0.
|
|
69
|
-
"@meith/notifications": "0.
|
|
70
|
-
"@meith/plugin-calendar": "0.
|
|
71
|
-
"@meith/plugin-dues": "0.
|
|
72
|
-
"@meith/plugin-kit": "0.
|
|
73
|
-
"@meith/polls": "0.
|
|
74
|
-
"@meith/posts": "0.
|
|
75
|
-
"@meith/profile-fields": "0.
|
|
76
|
-
"@meith/relations": "0.
|
|
77
|
-
"@meith/reputation": "0.
|
|
78
|
-
"@meith/runtime": "0.
|
|
79
|
-
"@meith/search": "0.
|
|
80
|
-
"@meith/settings": "0.
|
|
81
|
-
"@meith/signatures": "0.
|
|
82
|
-
"@meith/subscriptions": "0.
|
|
83
|
-
"@meith/tasks": "0.
|
|
84
|
-
"@meith/theme-clubhouse": "0.
|
|
85
|
-
"@meith/theme-default": "0.
|
|
86
|
-
"@meith/theme-kit": "0.
|
|
87
|
-
"@meith/theme-midnight": "0.
|
|
88
|
-
"@meith/theme-phasebook": "0.
|
|
89
|
-
"@meith/theme-raidframe": "0.
|
|
90
|
-
"@meith/threads": "0.
|
|
91
|
-
"@meith/ui": "0.
|
|
92
|
-
"@meith/upgrade": "0.
|
|
46
|
+
"@meith/accounts": "0.28.0",
|
|
47
|
+
"@meith/admin": "0.28.0",
|
|
48
|
+
"@meith/antispam": "0.28.0",
|
|
49
|
+
"@meith/api": "0.28.0",
|
|
50
|
+
"@meith/attachments": "0.28.0",
|
|
51
|
+
"@meith/authorization": "0.28.0",
|
|
52
|
+
"@meith/avatars": "0.28.0",
|
|
53
|
+
"@meith/core": "0.28.0",
|
|
54
|
+
"@meith/db": "0.28.0",
|
|
55
|
+
"@meith/demo": "0.28.0",
|
|
56
|
+
"@meith/drafts": "0.28.0",
|
|
57
|
+
"@meith/drivers": "0.28.0",
|
|
58
|
+
"@meith/events": "0.28.0",
|
|
59
|
+
"@meith/forums": "0.28.0",
|
|
60
|
+
"@meith/i18n": "0.28.0",
|
|
61
|
+
"@meith/groups": "0.28.0",
|
|
62
|
+
"@meith/import": "0.28.0",
|
|
63
|
+
"@meith/install": "0.28.0",
|
|
64
|
+
"@meith/mail": "0.28.0",
|
|
65
|
+
"@meith/markdown": "0.28.0",
|
|
66
|
+
"@meith/marketplace": "0.28.0",
|
|
67
|
+
"@meith/messages": "0.28.0",
|
|
68
|
+
"@meith/moderation": "0.28.0",
|
|
69
|
+
"@meith/notifications": "0.28.0",
|
|
70
|
+
"@meith/plugin-calendar": "0.28.0",
|
|
71
|
+
"@meith/plugin-dues": "0.28.0",
|
|
72
|
+
"@meith/plugin-kit": "0.28.0",
|
|
73
|
+
"@meith/polls": "0.28.0",
|
|
74
|
+
"@meith/posts": "0.28.0",
|
|
75
|
+
"@meith/profile-fields": "0.28.0",
|
|
76
|
+
"@meith/relations": "0.28.0",
|
|
77
|
+
"@meith/reputation": "0.28.0",
|
|
78
|
+
"@meith/runtime": "0.28.0",
|
|
79
|
+
"@meith/search": "0.28.0",
|
|
80
|
+
"@meith/settings": "0.28.0",
|
|
81
|
+
"@meith/signatures": "0.28.0",
|
|
82
|
+
"@meith/subscriptions": "0.28.0",
|
|
83
|
+
"@meith/tasks": "0.28.0",
|
|
84
|
+
"@meith/theme-clubhouse": "0.28.0",
|
|
85
|
+
"@meith/theme-default": "0.28.0",
|
|
86
|
+
"@meith/theme-kit": "0.28.0",
|
|
87
|
+
"@meith/theme-midnight": "0.28.0",
|
|
88
|
+
"@meith/theme-phasebook": "0.28.0",
|
|
89
|
+
"@meith/theme-raidframe": "0.28.0",
|
|
90
|
+
"@meith/threads": "0.28.0",
|
|
91
|
+
"@meith/ui": "0.28.0",
|
|
92
|
+
"@meith/upgrade": "0.28.0"
|
|
93
93
|
},
|
|
94
94
|
"scripts": {
|
|
95
95
|
"dev": "next dev",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useActionState } from 'react'
|
|
3
|
+
import { useActionState, useEffect, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { MatrixCell, MatrixRow } from '@meith/authorization'
|
|
6
6
|
import { cn, Disclosure } from '@meith/ui'
|
|
@@ -301,10 +301,19 @@ export function ForumPermissionRowForm({
|
|
|
301
301
|
copy: Copy
|
|
302
302
|
}) {
|
|
303
303
|
const [state, action] = useActionState(saveForumPermissionsAction, EMPTY_STATE)
|
|
304
|
+
const [dirty, setDirty] = useState(false)
|
|
305
|
+
|
|
306
|
+
useEffect(() => {
|
|
307
|
+
if (state.notice === 'saved') setDirty(false)
|
|
308
|
+
}, [state])
|
|
304
309
|
|
|
305
310
|
return (
|
|
306
|
-
<Disclosure
|
|
307
|
-
|
|
311
|
+
<Disclosure
|
|
312
|
+
summary={row.groupTitle}
|
|
313
|
+
aside={overridesAside(row, copy)}
|
|
314
|
+
className="overflow-visible"
|
|
315
|
+
>
|
|
316
|
+
<form action={action} className="flex flex-col gap-5" onChange={() => setDirty(true)}>
|
|
308
317
|
<FormError message={state.error} />
|
|
309
318
|
<input type="hidden" name="forumId" value={forumId} />
|
|
310
319
|
<input type="hidden" name="groupId" value={row.groupId} />
|
|
@@ -333,14 +342,20 @@ export function ForumPermissionRowForm({
|
|
|
333
342
|
</section>
|
|
334
343
|
))}
|
|
335
344
|
|
|
336
|
-
<div className="flex items-center gap-3">
|
|
337
|
-
<SubmitButton>
|
|
345
|
+
<div className="sticky bottom-0 -mx-4 -mb-4 flex items-center gap-3 rounded-b-lg border-t border-border bg-card/95 px-4 py-3 supports-[backdrop-filter]:bg-card/80 supports-[backdrop-filter]:backdrop-blur">
|
|
346
|
+
<SubmitButton className="w-auto">
|
|
338
347
|
{formatFromCopy(copy, 'adminForum.saveGroup', { group: row.groupTitle })}
|
|
339
348
|
</SubmitButton>
|
|
340
|
-
{
|
|
341
|
-
<span className="text-xs font-medium text-
|
|
342
|
-
{fromCopy(copy, '
|
|
349
|
+
{dirty ? (
|
|
350
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
351
|
+
{fromCopy(copy, 'adminForum.unsavedChanges')}
|
|
343
352
|
</span>
|
|
353
|
+
) : (
|
|
354
|
+
state.notice === 'saved' && (
|
|
355
|
+
<span className="text-xs font-medium text-moderation-approved">
|
|
356
|
+
{fromCopy(copy, 'admin.saved')}
|
|
357
|
+
</span>
|
|
358
|
+
)
|
|
344
359
|
)}
|
|
345
360
|
</div>
|
|
346
361
|
</form>
|
|
@@ -4,6 +4,7 @@ import { useActionState } from 'react'
|
|
|
4
4
|
|
|
5
5
|
import { EMPTY_STATE } from '@/server/auth-form-state'
|
|
6
6
|
import {
|
|
7
|
+
applyUpgradeAction,
|
|
7
8
|
clearCacheAction,
|
|
8
9
|
pruneSessionsAction,
|
|
9
10
|
pruneTokensAction,
|
|
@@ -42,6 +43,27 @@ export function PruneSessionsForm({ prunable, copy }: { prunable: number; copy:
|
|
|
42
43
|
)
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
export function ApplyMigrationsForm({ copy }: { copy: Copy }) {
|
|
47
|
+
const [state, action] = useActionState(applyUpgradeAction, EMPTY_STATE)
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<form action={action} className="flex flex-col gap-2">
|
|
51
|
+
<FormError message={state.error} />
|
|
52
|
+
{state.notice === 'upgraded' && (
|
|
53
|
+
<Result>
|
|
54
|
+
{formatFromCopy(copy, 'adminPanel.system.upgradeApplied', {
|
|
55
|
+
core: state.values?.core ?? '0',
|
|
56
|
+
plugins: state.values?.plugins === '' ? '—' : (state.values?.plugins ?? '—'),
|
|
57
|
+
})}
|
|
58
|
+
</Result>
|
|
59
|
+
)}
|
|
60
|
+
<div>
|
|
61
|
+
<SubmitButton>{fromCopy(copy, 'adminPanel.system.applyMigrations')}</SubmitButton>
|
|
62
|
+
</div>
|
|
63
|
+
</form>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
45
67
|
export function PruneTokensForm({ copy }: { copy: Copy }) {
|
|
46
68
|
const [state, action] = useActionState(pruneTokensAction, EMPTY_STATE)
|
|
47
69
|
|
|
@@ -131,7 +131,10 @@ export function MarkdownEditor({
|
|
|
131
131
|
const element = field.current
|
|
132
132
|
if (element === null) return
|
|
133
133
|
element.style.height = 'auto'
|
|
134
|
-
|
|
134
|
+
const max = Math.max(240, Math.round(window.innerHeight * 0.6))
|
|
135
|
+
const overflowing = element.scrollHeight > max
|
|
136
|
+
element.style.height = `${overflowing ? max : element.scrollHeight}px`
|
|
137
|
+
element.style.overflowY = overflowing ? 'auto' : 'hidden'
|
|
135
138
|
}, [])
|
|
136
139
|
|
|
137
140
|
useEffect(() => {
|
|
@@ -6,12 +6,13 @@ import { NotFoundError, ValidationError } from '@meith/core'
|
|
|
6
6
|
import { drivers } from '@meith/drivers'
|
|
7
7
|
import { msg } from '@meith/i18n'
|
|
8
8
|
|
|
9
|
-
import { recordAdminAction, requireAdmin } from './admin'
|
|
9
|
+
import { recordAdminAction, requireAdmin, requireFreshAdmin } from './admin'
|
|
10
10
|
import type { FormState } from './auth-form-state'
|
|
11
11
|
import { clearableTag } from './cache-targets'
|
|
12
12
|
import { formStateReporter } from './form-state-reporter'
|
|
13
13
|
import { requireSearch } from './search'
|
|
14
14
|
import { requireMaintenance, requireRecount } from './system-admin'
|
|
15
|
+
import { applyPendingUpgrade } from './upgrade-notice'
|
|
15
16
|
|
|
16
17
|
const SWEEP_LIMIT = 5_000
|
|
17
18
|
|
|
@@ -89,6 +90,28 @@ export async function reindexSearchAction(): Promise<FormState> {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
export async function applyUpgradeAction(): Promise<FormState> {
|
|
94
|
+
try {
|
|
95
|
+
await requireFreshAdmin()
|
|
96
|
+
|
|
97
|
+
const result = await applyPendingUpgrade()
|
|
98
|
+
|
|
99
|
+
refreshSystemScreen()
|
|
100
|
+
revalidatePath('/admin')
|
|
101
|
+
await recordAdminAction({
|
|
102
|
+
action: 'system.upgrade_applied',
|
|
103
|
+
detail: { coreMigrations: result.coreMigrations, plugins: result.plugins },
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
notice: 'upgraded',
|
|
108
|
+
values: { core: String(result.coreMigrations), plugins: result.plugins.join(', ') },
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {
|
|
111
|
+
return toFormState(err)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
92
115
|
export async function clearCacheAction(_prev: FormState, form: FormData): Promise<FormState> {
|
|
93
116
|
try {
|
|
94
117
|
await requireAdmin()
|
|
@@ -1,12 +1,94 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
|
|
3
|
-
import { env, logger } from '@meith/core'
|
|
4
|
-
import {
|
|
3
|
+
import { env, logger, ValidationError } from '@meith/core'
|
|
4
|
+
import {
|
|
5
|
+
appliedPluginMigrations,
|
|
6
|
+
applyPluginMigration,
|
|
7
|
+
getDb,
|
|
8
|
+
PostgresNavigationRepository,
|
|
9
|
+
readVersion,
|
|
10
|
+
recordVersion,
|
|
11
|
+
runMigrations,
|
|
12
|
+
} from '@meith/db'
|
|
13
|
+
import { msg } from '@meith/i18n'
|
|
14
|
+
import { pluginNavigationPlacements } from '@meith/plugin-kit'
|
|
15
|
+
import { runPluginLifecycle } from '@meith/runtime'
|
|
5
16
|
import { planUpgrade, type UpgradeState, upgradeNotice } from '@meith/upgrade'
|
|
6
17
|
|
|
7
18
|
import { activeDefinitions } from './plugin-host'
|
|
8
19
|
|
|
9
|
-
export const CODE_VERSION = '0.
|
|
20
|
+
export const CODE_VERSION = '0.28.0'
|
|
21
|
+
|
|
22
|
+
export interface UpgradeApplied {
|
|
23
|
+
readonly coreMigrations: number
|
|
24
|
+
readonly plugins: readonly string[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function applyPendingUpgrade(): Promise<UpgradeApplied> {
|
|
28
|
+
const db = getDb()
|
|
29
|
+
const definitions = activeDefinitions()
|
|
30
|
+
|
|
31
|
+
const applied: Record<string, readonly string[]> = {}
|
|
32
|
+
for (const plugin of definitions) {
|
|
33
|
+
applied[plugin.key] = await appliedPluginMigrations(db, plugin.key)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const state: UpgradeState = {
|
|
37
|
+
recordedVersion: (await readVersion(db, 'core')) ?? CODE_VERSION,
|
|
38
|
+
codeVersion: CODE_VERSION,
|
|
39
|
+
pendingCoreMigrations: [],
|
|
40
|
+
plugins: definitions.map((plugin) => ({
|
|
41
|
+
key: plugin.key,
|
|
42
|
+
version: plugin.version,
|
|
43
|
+
dependsOn: plugin.dependsOn ?? [],
|
|
44
|
+
migrationIds: (plugin.migrations ?? []).map((migration) => migration.id),
|
|
45
|
+
})),
|
|
46
|
+
appliedPluginMigrations: applied,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const plan = planUpgrade(state)
|
|
50
|
+
if (plan.refusal !== null || plan.orderFailure !== null) {
|
|
51
|
+
const notice = upgradeNotice(plan, state)
|
|
52
|
+
throw notice === null
|
|
53
|
+
? new ValidationError(msg('error.app.board-cannot-be-upgraded'))
|
|
54
|
+
: new ValidationError(notice)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const fresh = new Set<string>()
|
|
58
|
+
for (const plugin of definitions) {
|
|
59
|
+
if ((await readVersion(db, `plugin:${plugin.key}`)) === null) fresh.add(plugin.key)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const coreMigrations = await runMigrations()
|
|
63
|
+
|
|
64
|
+
const touched: string[] = []
|
|
65
|
+
for (const definition of definitions) {
|
|
66
|
+
let changed = false
|
|
67
|
+
for (const migration of definition.migrations ?? []) {
|
|
68
|
+
const ran = await applyPluginMigration(db, definition.key, migration.id, migration.statements)
|
|
69
|
+
if (ran) changed = true
|
|
70
|
+
}
|
|
71
|
+
if (fresh.has(definition.key) && definition.onInstall !== undefined) {
|
|
72
|
+
const { ran } = await runPluginLifecycle({ db, plugin: definition, phase: 'install' })
|
|
73
|
+
if (ran) changed = true
|
|
74
|
+
}
|
|
75
|
+
await recordVersion(db, `plugin:${definition.key}`, definition.version)
|
|
76
|
+
if (changed) touched.push(definition.key)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await new PostgresNavigationRepository(db).syncPluginItems(
|
|
80
|
+
pluginNavigationPlacements(definitions).map((item) => ({
|
|
81
|
+
key: item.key,
|
|
82
|
+
href: item.href,
|
|
83
|
+
audience: item.audience,
|
|
84
|
+
parentKey: item.parentKey,
|
|
85
|
+
})),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
await recordVersion(db, 'core', CODE_VERSION)
|
|
89
|
+
|
|
90
|
+
return { coreMigrations, plugins: touched }
|
|
91
|
+
}
|
|
10
92
|
|
|
11
93
|
export async function pendingUpgradeNotice(): Promise<string | null> {
|
|
12
94
|
if (env.DATA_SOURCE !== 'postgres') return null
|
package/src/view/admin-copy.ts
CHANGED
|
@@ -63,6 +63,7 @@ export function forumAdminCopy(t: Translator = untranslated()): Readonly<Record<
|
|
|
63
63
|
'adminForum.permGroup.approvals',
|
|
64
64
|
'adminForum.permGroup.other',
|
|
65
65
|
'adminForum.inheritsAll',
|
|
66
|
+
'adminForum.unsavedChanges',
|
|
66
67
|
'adminForum.copied',
|
|
67
68
|
'adminForum.copyNote',
|
|
68
69
|
'adminForum.topLevel',
|
|
@@ -60,6 +60,7 @@ export function systemFormsCopy(t: Translator = untranslated()): Readonly<Record
|
|
|
60
60
|
'adminPanel.system.jobIdHint',
|
|
61
61
|
'adminPanel.system.retry',
|
|
62
62
|
'adminPanel.system.nothingToIndex',
|
|
63
|
+
'adminPanel.system.applyMigrations',
|
|
63
64
|
],
|
|
64
65
|
t,
|
|
65
66
|
),
|
|
@@ -73,6 +74,7 @@ export function systemFormsCopy(t: Translator = untranslated()): Readonly<Record
|
|
|
73
74
|
'adminPanel.system.indexFinished',
|
|
74
75
|
'adminPanel.system.indexMore',
|
|
75
76
|
'adminPanel.system.indexNext',
|
|
77
|
+
'adminPanel.system.upgradeApplied',
|
|
76
78
|
],
|
|
77
79
|
t,
|
|
78
80
|
),
|
|
@@ -13,7 +13,6 @@ export const GROUP_LABELS: Record<SettingGroup, string> = {
|
|
|
13
13
|
antispam: 'Anti-spam',
|
|
14
14
|
push: 'Push',
|
|
15
15
|
legal: 'Legal',
|
|
16
|
-
marketplace: 'Marketplace',
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
export const GROUP_ORDER: readonly SettingGroup[] = [
|
|
@@ -29,7 +28,6 @@ export const GROUP_ORDER: readonly SettingGroup[] = [
|
|
|
29
28
|
'antispam',
|
|
30
29
|
'push',
|
|
31
30
|
'legal',
|
|
32
|
-
'marketplace',
|
|
33
31
|
]
|
|
34
32
|
|
|
35
33
|
export const DEFAULT_SETTING_GROUP: SettingGroup = GROUP_ORDER[0]!
|