@meith/web 0.36.2 → 0.37.1
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/fixtures/page.tsx +30 -0
- package/app/layout.tsx +0 -2
- package/next.config.mjs +1 -1
- package/package.json +51 -51
- package/src/server/auth-config.ts +2 -13
- package/src/server/board-latest.tsx +5 -3
- package/src/server/container.ts +16 -38
- package/src/server/federation-actions.ts +0 -3
- package/src/server/fixture-activity-repo.ts +127 -0
- package/src/server/fixture-content.json +3045 -0
- package/src/server/navigation.ts +6 -2
- package/src/server/plugin-routes.ts +1 -2
- package/src/server/request-fingerprint.ts +2 -15
- package/src/server/seed-board.ts +180 -29
- package/src/server/stats.ts +5 -4
- package/src/server/syndication.ts +1 -3
- package/src/server/two-factor-actions.ts +0 -3
- package/src/server/upgrade-notice.ts +1 -1
- package/src/server/user-admin-actions.ts +0 -4
- package/src/server/usercp-actions.ts +0 -5
- package/src/theme/contract.fixture.ts +6 -6
- package/src/theme/gallery.fixture.tsx +412 -0
- package/src/theme/preview-boundary.tsx +23 -0
- package/src/theme/preview.fixture.ts +304 -0
- package/app/demo/stripe/[[...path]]/route.ts +0 -27
- package/src/components/shell/demo-banner.tsx +0 -41
- package/src/server/demo-stripe.ts +0 -104
- package/src/server/demo-uploads.ts +0 -17
- package/src/server/demo.ts +0 -59
package/src/server/navigation.ts
CHANGED
|
@@ -79,12 +79,16 @@ export async function boardNavigation(
|
|
|
79
79
|
const live = await Promise.all(items.map((item) => pluginRowIsLive(item.key)))
|
|
80
80
|
const shown = items.filter((_, index) => live[index] === true)
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
const t = await getTranslator()
|
|
83
|
+
const links = buildNavigation(shown, viewer, {
|
|
83
84
|
searchEnabled: await searchEnabled(),
|
|
84
|
-
t
|
|
85
|
+
t,
|
|
85
86
|
names: pluginNavigationNames(),
|
|
86
87
|
admits: (item) => authorizer.inAnyGroup(actor, item.visibleToGroups),
|
|
87
88
|
})
|
|
89
|
+
return env.DATA_SOURCE === 'fixture'
|
|
90
|
+
? [...links, { label: t.t('nav.themeFixtures'), href: '/fixtures' }]
|
|
91
|
+
: links
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
export async function syncPluginNavigation(): Promise<void> {
|
|
@@ -3,7 +3,6 @@ import 'server-only'
|
|
|
3
3
|
|
|
4
4
|
import { env, logger, resolveClientAddress } from '@meith/core'
|
|
5
5
|
import { currentRequestId } from '@meith/core/logger'
|
|
6
|
-
import { demoAddressToken, demoDiscardsAddresses } from '@meith/demo'
|
|
7
6
|
import {
|
|
8
7
|
createRouteRateLimiter,
|
|
9
8
|
DEFAULT_ROUTE_BODY_BYTES,
|
|
@@ -169,7 +168,7 @@ function callerKey(request: Request, userId: number | null): string {
|
|
|
169
168
|
)
|
|
170
169
|
if (address === null) return 'a:unknown'
|
|
171
170
|
|
|
172
|
-
return `a:${
|
|
171
|
+
return `a:${address}`
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
export type PluginRouteSurface = 'board' | 'admin'
|
|
@@ -3,7 +3,6 @@ import 'server-only'
|
|
|
3
3
|
import { headers } from 'next/headers'
|
|
4
4
|
|
|
5
5
|
import { env, logger, resolveClientAddress, truncateIp } from '@meith/core'
|
|
6
|
-
import { demoAddressToken, demoDiscardsAddresses } from '@meith/demo'
|
|
7
6
|
|
|
8
7
|
export interface RequestFingerprint {
|
|
9
8
|
readonly ipPrefix: string | null
|
|
@@ -32,25 +31,13 @@ export async function remoteAddress(): Promise<string | null> {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export async function retainedIpPrefix(): Promise<string | null> {
|
|
35
|
-
if (demoDiscardsAddresses()) return null
|
|
36
34
|
return truncateIp(await remoteAddress()) ?? null
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
export
|
|
40
|
-
const address = await remoteAddress()
|
|
41
|
-
if (address === null || address === '') return null
|
|
42
|
-
|
|
43
|
-
const prefix = truncateIp(address)
|
|
44
|
-
if (prefix === undefined) return null
|
|
45
|
-
|
|
46
|
-
return demoDiscardsAddresses() ? demoAddressToken(prefix) : prefix
|
|
47
|
-
}
|
|
37
|
+
export const countingPrefix = retainedIpPrefix
|
|
48
38
|
|
|
49
39
|
export async function countingAddress(): Promise<string | null> {
|
|
50
|
-
|
|
51
|
-
if (address === null || address === '') return null
|
|
52
|
-
|
|
53
|
-
return demoDiscardsAddresses() ? demoAddressToken(address) : address
|
|
40
|
+
return (await remoteAddress()) || null
|
|
54
41
|
}
|
|
55
42
|
|
|
56
43
|
export async function requestFingerprint(): Promise<RequestFingerprint> {
|
package/src/server/seed-board.ts
CHANGED
|
@@ -2,9 +2,11 @@ import type { MemberProfileRecord } from '@meith/accounts'
|
|
|
2
2
|
import type { GroupDefaults, MemoryBoard } from '@meith/authorization'
|
|
3
3
|
import { emptyPermissionSet, type ForumPermissions, type PermissionSet } from '@meith/core'
|
|
4
4
|
import type { ForumListingRow } from '@meith/forums'
|
|
5
|
-
import { BodyFormat } from '@meith/markdown'
|
|
5
|
+
import { BodyFormat, quoteBlock } from '@meith/markdown'
|
|
6
6
|
import type { PostListingRow } from '@meith/posts'
|
|
7
|
-
import type
|
|
7
|
+
import { type ThreadListingRow, threadSlug } from '@meith/threads'
|
|
8
|
+
|
|
9
|
+
import content from './fixture-content.json'
|
|
8
10
|
|
|
9
11
|
export const SEED_GROUP = {
|
|
10
12
|
guest: 1,
|
|
@@ -16,7 +18,7 @@ export const SEED_GROUP = {
|
|
|
16
18
|
banned: 7,
|
|
17
19
|
} as const
|
|
18
20
|
|
|
19
|
-
export const FIXTURE_DATA_VERSION =
|
|
21
|
+
export const FIXTURE_DATA_VERSION = 5
|
|
20
22
|
|
|
21
23
|
export const SEED_FORUM = {
|
|
22
24
|
main: 10,
|
|
@@ -88,29 +90,7 @@ const ANNOUNCEMENT_READONLY: Partial<ForumPermissions> = {
|
|
|
88
90
|
canPostReplies: false,
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
groups: GROUPS,
|
|
93
|
-
chains: {
|
|
94
|
-
[SEED_FORUM.main]: [SEED_FORUM.main],
|
|
95
|
-
[SEED_FORUM.announcements]: [SEED_FORUM.announcements, SEED_FORUM.main],
|
|
96
|
-
[SEED_FORUM.general]: [SEED_FORUM.general, SEED_FORUM.main],
|
|
97
|
-
[SEED_FORUM.generalOffTopic]: [SEED_FORUM.generalOffTopic, SEED_FORUM.general, SEED_FORUM.main],
|
|
98
|
-
},
|
|
99
|
-
overrides: [
|
|
100
|
-
{
|
|
101
|
-
forumId: SEED_FORUM.announcements,
|
|
102
|
-
groupId: SEED_GROUP.guest,
|
|
103
|
-
overrides: ANNOUNCEMENT_READONLY,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
forumId: SEED_FORUM.announcements,
|
|
107
|
-
groupId: SEED_GROUP.registered,
|
|
108
|
-
overrides: ANNOUNCEMENT_READONLY,
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export const SEED_FORUM_ROWS: readonly ForumListingRow[] = [
|
|
93
|
+
const FORUM_ROWS: ForumListingRow[] = [
|
|
114
94
|
{
|
|
115
95
|
id: SEED_FORUM.main,
|
|
116
96
|
type: 'category',
|
|
@@ -198,7 +178,7 @@ export const SEED_FORUM_ROWS: readonly ForumListingRow[] = [
|
|
|
198
178
|
},
|
|
199
179
|
]
|
|
200
180
|
|
|
201
|
-
|
|
181
|
+
const THREAD_ROWS: ThreadListingRow[] = [
|
|
202
182
|
{
|
|
203
183
|
id: 4,
|
|
204
184
|
forumId: SEED_FORUM.announcements,
|
|
@@ -273,7 +253,7 @@ export const SEED_THREAD_ROWS: readonly ThreadListingRow[] = [
|
|
|
273
253
|
},
|
|
274
254
|
]
|
|
275
255
|
|
|
276
|
-
|
|
256
|
+
const POST_ROWS: PostListingRow[] = [
|
|
277
257
|
{
|
|
278
258
|
id: 10,
|
|
279
259
|
threadId: 4,
|
|
@@ -402,7 +382,7 @@ export const SEED_POST_ROWS: readonly PostListingRow[] = [
|
|
|
402
382
|
},
|
|
403
383
|
]
|
|
404
384
|
|
|
405
|
-
|
|
385
|
+
const MEMBER_PROFILES: MemberProfileRecord[] = [
|
|
406
386
|
{
|
|
407
387
|
id: 1,
|
|
408
388
|
username: 'admin',
|
|
@@ -415,3 +395,174 @@ export const SEED_MEMBER_PROFILES: readonly MemberProfileRecord[] = [
|
|
|
415
395
|
bio: 'Runs this board. Fixture data — nothing here is durable.',
|
|
416
396
|
},
|
|
417
397
|
]
|
|
398
|
+
|
|
399
|
+
const SNAPSHOT_AT = new Date('2026-07-30T12:00:00Z')
|
|
400
|
+
const forumIds = new Map(content.forums.map((forum, index) => [forum.key, 1000 + index]))
|
|
401
|
+
const memberIds = new Map(
|
|
402
|
+
content.members.map((member, index) => [member.key, member.key === 'admin' ? 1 : 100 + index]),
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
for (const member of content.members) {
|
|
406
|
+
const id = memberIds.get(member.key)!
|
|
407
|
+
if (id === 1) continue
|
|
408
|
+
MEMBER_PROFILES.push({
|
|
409
|
+
id,
|
|
410
|
+
username: member.username,
|
|
411
|
+
title: 'Members',
|
|
412
|
+
postCount: 0,
|
|
413
|
+
createdAt: new Date(SNAPSHOT_AT.getTime() - member.joinedDaysAgo * 86_400_000),
|
|
414
|
+
lastActiveAt: SNAPSHOT_AT,
|
|
415
|
+
location: member.location,
|
|
416
|
+
website: member.website,
|
|
417
|
+
bio: member.bio,
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
for (const [index, forum] of content.forums.entries()) {
|
|
422
|
+
const id = forumIds.get(forum.key)!
|
|
423
|
+
const parentId = forum.parent === null ? null : forumIds.get(forum.parent)!
|
|
424
|
+
FORUM_ROWS.push({
|
|
425
|
+
id,
|
|
426
|
+
type: forum.type as 'category' | 'forum',
|
|
427
|
+
allowThreads: forum.type === 'forum',
|
|
428
|
+
title: forum.title,
|
|
429
|
+
slug: forum.slug,
|
|
430
|
+
description: forum.description ?? null,
|
|
431
|
+
parentId,
|
|
432
|
+
path: parentId === null ? String(id) : `${parentId}.${id}`,
|
|
433
|
+
depth: parentId === null ? 0 : 1,
|
|
434
|
+
displayOrder: 10 + index,
|
|
435
|
+
linkUrl: null,
|
|
436
|
+
threadCount: 0,
|
|
437
|
+
postCount: 0,
|
|
438
|
+
lastPost: null,
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
for (const [index, thread] of content.threads.entries()) {
|
|
443
|
+
const id = 1000 + index
|
|
444
|
+
const forumId = forumIds.get(thread.forum)!
|
|
445
|
+
const createdAt = new Date(SNAPSHOT_AT.getTime() - thread.daysAgo * 86_400_000)
|
|
446
|
+
const slug = threadSlug(thread.title)
|
|
447
|
+
const posts: PostListingRow[] = []
|
|
448
|
+
for (const [number, reply] of [{ ...thread, hoursAfter: 0 }, ...thread.replies].entries()) {
|
|
449
|
+
const member = MEMBER_PROFILES.find((profile) => profile.id === memberIds.get(reply.author))!
|
|
450
|
+
const quoted = 'quotes' in reply && reply.quotes !== undefined ? posts[reply.quotes] : undefined
|
|
451
|
+
const message =
|
|
452
|
+
quoted === undefined
|
|
453
|
+
? reply.message
|
|
454
|
+
: `${quoteBlock({
|
|
455
|
+
author: quoted.authorUsername,
|
|
456
|
+
markdown: quoted.message,
|
|
457
|
+
sourceHref: `/thread/${id}-${slug}?post=${quoted.id}`,
|
|
458
|
+
})}\n\n${reply.message}`
|
|
459
|
+
posts.push({
|
|
460
|
+
id: 1000 + POST_ROWS.length + number,
|
|
461
|
+
threadId: id,
|
|
462
|
+
forumId,
|
|
463
|
+
number: number + 1,
|
|
464
|
+
authorUserId: member.id,
|
|
465
|
+
authorUsername: member.username,
|
|
466
|
+
authorPostCount: 0,
|
|
467
|
+
authorJoinedAt: member.createdAt,
|
|
468
|
+
message,
|
|
469
|
+
messageHtml: null,
|
|
470
|
+
renderVersion: 0,
|
|
471
|
+
bodyFormat: BodyFormat.Markdown,
|
|
472
|
+
editedAt: null,
|
|
473
|
+
editedByUsername: null,
|
|
474
|
+
editReason: null,
|
|
475
|
+
isFirstPost: number === 0,
|
|
476
|
+
visibility: 'visible',
|
|
477
|
+
createdAt: new Date(createdAt.getTime() + reply.hoursAfter * 3_600_000),
|
|
478
|
+
})
|
|
479
|
+
}
|
|
480
|
+
POST_ROWS.push(...posts)
|
|
481
|
+
const first = posts[0]!
|
|
482
|
+
const last = posts.at(-1)!
|
|
483
|
+
const prefix =
|
|
484
|
+
'prefix' in thread ? content.prefixes.find((prefix) => prefix.key === thread.prefix) : undefined
|
|
485
|
+
THREAD_ROWS.push({
|
|
486
|
+
id,
|
|
487
|
+
forumId,
|
|
488
|
+
title: thread.title,
|
|
489
|
+
slug,
|
|
490
|
+
prefix: prefix === undefined ? null : { label: prefix.label, token: prefix.token },
|
|
491
|
+
authorUserId: first.authorUserId,
|
|
492
|
+
authorUsername: first.authorUsername,
|
|
493
|
+
replyCount: posts.length - 1,
|
|
494
|
+
viewCount: posts.length * 37,
|
|
495
|
+
ratingTotal: 0,
|
|
496
|
+
ratingCount: 0,
|
|
497
|
+
visibility: 'visible',
|
|
498
|
+
isSticky: 'sticky' in thread && thread.sticky === true,
|
|
499
|
+
isLocked: 'locked' in thread && thread.locked === true,
|
|
500
|
+
isMoved: false,
|
|
501
|
+
lastPost: {
|
|
502
|
+
postId: last.id,
|
|
503
|
+
userId: last.authorUserId,
|
|
504
|
+
username: last.authorUsername,
|
|
505
|
+
at: last.createdAt,
|
|
506
|
+
},
|
|
507
|
+
lastPostAt: last.createdAt,
|
|
508
|
+
})
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export const SEED_THREAD_ROWS: readonly ThreadListingRow[] = THREAD_ROWS
|
|
512
|
+
export const SEED_MEMBER_PROFILES: readonly MemberProfileRecord[] = MEMBER_PROFILES.map(
|
|
513
|
+
(member) => ({
|
|
514
|
+
...member,
|
|
515
|
+
postCount: POST_ROWS.filter((post) => post.authorUserId === member.id).length,
|
|
516
|
+
}),
|
|
517
|
+
)
|
|
518
|
+
export const SEED_POST_ROWS: readonly PostListingRow[] = POST_ROWS.map((post) => ({
|
|
519
|
+
...post,
|
|
520
|
+
authorPostCount:
|
|
521
|
+
SEED_MEMBER_PROFILES.find((member) => member.id === post.authorUserId)?.postCount ?? 0,
|
|
522
|
+
}))
|
|
523
|
+
export const SEED_FORUM_ROWS: readonly ForumListingRow[] = FORUM_ROWS.map((forum) => {
|
|
524
|
+
const beneath = new Set(
|
|
525
|
+
FORUM_ROWS.filter(
|
|
526
|
+
(row) => row.path === forum.path || row.path.startsWith(`${forum.path}.`),
|
|
527
|
+
).map((row) => row.id),
|
|
528
|
+
)
|
|
529
|
+
const posts = POST_ROWS.filter((post) => beneath.has(post.forumId))
|
|
530
|
+
const last = [...posts].sort(
|
|
531
|
+
(a, b) => b.createdAt.getTime() - a.createdAt.getTime() || b.id - a.id,
|
|
532
|
+
)[0]
|
|
533
|
+
return {
|
|
534
|
+
...forum,
|
|
535
|
+
threadCount: THREAD_ROWS.filter((thread) => beneath.has(thread.forumId)).length,
|
|
536
|
+
postCount: posts.length,
|
|
537
|
+
lastPost:
|
|
538
|
+
last === undefined
|
|
539
|
+
? null
|
|
540
|
+
: {
|
|
541
|
+
postId: last.id,
|
|
542
|
+
threadId: last.threadId,
|
|
543
|
+
threadTitle: THREAD_ROWS.find((thread) => thread.id === last.threadId)!.title,
|
|
544
|
+
userId: last.authorUserId,
|
|
545
|
+
username: last.authorUsername,
|
|
546
|
+
at: last.createdAt,
|
|
547
|
+
},
|
|
548
|
+
}
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
export const SEED_BOARD: MemoryBoard = {
|
|
552
|
+
groups: GROUPS,
|
|
553
|
+
chains: Object.fromEntries(
|
|
554
|
+
SEED_FORUM_ROWS.map((forum) => [forum.id, forum.path.split('.').map(Number).reverse()]),
|
|
555
|
+
),
|
|
556
|
+
overrides: [
|
|
557
|
+
{
|
|
558
|
+
forumId: SEED_FORUM.announcements,
|
|
559
|
+
groupId: SEED_GROUP.guest,
|
|
560
|
+
overrides: ANNOUNCEMENT_READONLY,
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
forumId: SEED_FORUM.announcements,
|
|
564
|
+
groupId: SEED_GROUP.registered,
|
|
565
|
+
overrides: ANNOUNCEMENT_READONLY,
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
}
|
package/src/server/stats.ts
CHANGED
|
@@ -12,11 +12,14 @@ import {
|
|
|
12
12
|
} from '@meith/db'
|
|
13
13
|
|
|
14
14
|
import { getContainer } from './container'
|
|
15
|
+
import { FixtureActivityRepository } from './fixture-activity-repo'
|
|
15
16
|
|
|
16
17
|
export const LEADERBOARD_SIZE = 10
|
|
17
18
|
|
|
18
|
-
export function statsRepository(): PostgresStatsRepository |
|
|
19
|
-
return getContainer().dataSource === 'postgres'
|
|
19
|
+
export function statsRepository(): PostgresStatsRepository | FixtureActivityRepository {
|
|
20
|
+
return getContainer().dataSource === 'postgres'
|
|
21
|
+
? new PostgresStatsRepository(getDb())
|
|
22
|
+
: new FixtureActivityRepository()
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export async function statsScopeFor(actor: Actor): Promise<StatsScope> {
|
|
@@ -38,7 +41,6 @@ export interface StatsView {
|
|
|
38
41
|
|
|
39
42
|
export async function buildStatsView(actor: Actor): Promise<StatsView | null> {
|
|
40
43
|
const repo = statsRepository()
|
|
41
|
-
if (repo === null) return null
|
|
42
44
|
|
|
43
45
|
const scope = await statsScopeFor(actor)
|
|
44
46
|
const [totals, topPosters, mostViewed, mostReplied] = await Promise.all([
|
|
@@ -53,7 +55,6 @@ export async function buildStatsView(actor: Actor): Promise<StatsView | null> {
|
|
|
53
55
|
|
|
54
56
|
export async function readTotals(): Promise<BoardTotals | null> {
|
|
55
57
|
const repo = statsRepository()
|
|
56
|
-
if (repo === null) return null
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
60
|
return await repo.readTotals()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PUBLIC_CONTENT } from '@meith/core'
|
|
4
4
|
import { type FeedScope, getDb, PostgresFeedRepository } from '@meith/db'
|
|
5
5
|
|
|
6
6
|
import type { SitemapUrl } from '@/view/feed'
|
|
@@ -41,8 +41,6 @@ export async function publicScope(): Promise<FeedScope> {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export async function isIndexable(): Promise<boolean> {
|
|
44
|
-
if (env.DEMO_MODE) return false
|
|
45
|
-
|
|
46
44
|
try {
|
|
47
45
|
return (await getSettings()).get('board.offline') !== true
|
|
48
46
|
} catch {
|
|
@@ -13,7 +13,6 @@ import type { FormState } from './auth-form-state'
|
|
|
13
13
|
import { getContainer } from './container'
|
|
14
14
|
import { getActor } from './context'
|
|
15
15
|
import { requireFreshCredentialProof } from './credential-proof'
|
|
16
|
-
import { assertDemoAccountChangeable } from './demo'
|
|
17
16
|
import { formStateReporter } from './form-state-reporter'
|
|
18
17
|
import { text } from './form-values'
|
|
19
18
|
import { getSettingsUncached } from './settings'
|
|
@@ -33,8 +32,6 @@ async function ownAccount(): Promise<Owner> {
|
|
|
33
32
|
const actor = await getActor()
|
|
34
33
|
if (actor.userId === null) throw new ForbiddenError(msg('error.app.must-logged'))
|
|
35
34
|
|
|
36
|
-
await assertDemoAccountChangeable(actor.userId, 'sign-in method')
|
|
37
|
-
|
|
38
35
|
const account = await getContainer().accountStore.accounts.findById(actor.userId)
|
|
39
36
|
if (account === null) throw new ForbiddenError(msg('error.app.account-longer-exists'))
|
|
40
37
|
|
|
@@ -17,7 +17,7 @@ import { planUpgrade, type UpgradeState, upgradeNotice } from '@meith/upgrade'
|
|
|
17
17
|
|
|
18
18
|
import { activeDefinitions } from './plugin-host'
|
|
19
19
|
|
|
20
|
-
export const CODE_VERSION = '0.
|
|
20
|
+
export const CODE_VERSION = '0.37.1'
|
|
21
21
|
|
|
22
22
|
export interface UpgradeApplied {
|
|
23
23
|
readonly plugins: readonly string[]
|
|
@@ -12,7 +12,6 @@ import { claimAdminUndo, issueAdminUndo } from './admin-undo'
|
|
|
12
12
|
import { recordStaffAuthEvent } from './auth-events'
|
|
13
13
|
import type { FormState } from './auth-form-state'
|
|
14
14
|
import { getContainer } from './container'
|
|
15
|
-
import { assertDemoAccountChangeable, assertDemoIdentityUnchanged } from './demo'
|
|
16
15
|
import { formStateReporter } from './form-state-reporter'
|
|
17
16
|
import { trimmedText } from './form-values'
|
|
18
17
|
import { getTranslator } from './i18n'
|
|
@@ -64,7 +63,6 @@ export async function saveMemberAccountAction(
|
|
|
64
63
|
|
|
65
64
|
const username = trimmedText(form, 'username')
|
|
66
65
|
const email = trimmedText(form, 'email')
|
|
67
|
-
await assertDemoIdentityUnchanged(id, { username, email })
|
|
68
66
|
|
|
69
67
|
const before = await getContainer().accountStore.accounts.findById(id)
|
|
70
68
|
|
|
@@ -102,8 +100,6 @@ export async function clearSecondFactorAction(
|
|
|
102
100
|
await requireFreshAdmin()
|
|
103
101
|
const id = userId(form)
|
|
104
102
|
|
|
105
|
-
await assertDemoAccountChangeable(id, 'sign-in method')
|
|
106
|
-
|
|
107
103
|
const cleared = await clearMemberSecondFactor(id)
|
|
108
104
|
if (!cleared) return { notice: 'cleared' }
|
|
109
105
|
|
|
@@ -17,7 +17,6 @@ import type { FormState } from './auth-form-state'
|
|
|
17
17
|
import { AVATAR_FIELD, canUploadAvatar, requireAvatarService } from './avatars'
|
|
18
18
|
import { configuredSessions, getContainer } from './container'
|
|
19
19
|
import { getActor } from './context'
|
|
20
|
-
import { assertDemoAccountChangeable } from './demo'
|
|
21
20
|
import { revokeFeedToken } from './feed-token'
|
|
22
21
|
import { formStateReporter } from './form-state-reporter'
|
|
23
22
|
import { text } from './form-values'
|
|
@@ -150,8 +149,6 @@ export async function changePasswordAction(_prev: FormState, form: FormData): Pr
|
|
|
150
149
|
try {
|
|
151
150
|
const { service, userId } = await requireOwnSettings()
|
|
152
151
|
|
|
153
|
-
await assertDemoAccountChangeable(userId, 'password')
|
|
154
|
-
|
|
155
152
|
const next = text(form, 'newPassword')
|
|
156
153
|
if (next !== text(form, 'confirmPassword')) {
|
|
157
154
|
return { error: await tr('notice.app.two-new-passwords-match') }
|
|
@@ -189,8 +186,6 @@ export async function requestEmailChangeAction(
|
|
|
189
186
|
try {
|
|
190
187
|
const { service, userId } = await requireOwnSettings()
|
|
191
188
|
|
|
192
|
-
await assertDemoAccountChangeable(userId, 'email')
|
|
193
|
-
|
|
194
189
|
pending = {
|
|
195
190
|
userId,
|
|
196
191
|
...(await service.requestEmailChange({
|
|
@@ -10,7 +10,7 @@ const VIEWER = {
|
|
|
10
10
|
userId: 7,
|
|
11
11
|
username: 'Wren',
|
|
12
12
|
profileHref: '/member/7-wren',
|
|
13
|
-
avatarUrl: '/
|
|
13
|
+
avatarUrl: '/placeholder-user.jpg',
|
|
14
14
|
canAccessAdminCp: false,
|
|
15
15
|
canAccessModCp: true,
|
|
16
16
|
}
|
|
@@ -64,7 +64,7 @@ const THREAD = {
|
|
|
64
64
|
|
|
65
65
|
const POST_AUTHOR = {
|
|
66
66
|
...AUTHOR,
|
|
67
|
-
avatarUrl: '/
|
|
67
|
+
avatarUrl: '/placeholder-user.jpg',
|
|
68
68
|
title: 'Registered',
|
|
69
69
|
groups: [
|
|
70
70
|
{ title: 'Registered', nameClass: null },
|
|
@@ -105,8 +105,8 @@ const POST = {
|
|
|
105
105
|
filename: 'plan.png',
|
|
106
106
|
size: '1.4 MB',
|
|
107
107
|
isImage: true,
|
|
108
|
-
href: '/
|
|
109
|
-
thumbnailHref: '/
|
|
108
|
+
href: '/placeholder.jpg',
|
|
109
|
+
thumbnailHref: '/placeholder.jpg',
|
|
110
110
|
width: 800,
|
|
111
111
|
height: 600,
|
|
112
112
|
},
|
|
@@ -136,7 +136,7 @@ export interface SlotFixture<K extends SlotName> {
|
|
|
136
136
|
readonly requires: readonly string[]
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export const SLOT_FIXTURES: { readonly [K in SlotName]
|
|
139
|
+
export const SLOT_FIXTURES: { readonly [K in SlotName]: SlotFixture<K> } = {
|
|
140
140
|
Shell: {
|
|
141
141
|
model: { boardTitle: 'The Bike Shed', viewer: GUEST, children: region('body') },
|
|
142
142
|
requires: [region('body')],
|
|
@@ -472,7 +472,7 @@ export const SLOT_FIXTURES: { readonly [K in SlotName]?: SlotFixture<K> } = {
|
|
|
472
472
|
MemberProfile: {
|
|
473
473
|
model: {
|
|
474
474
|
user: AUTHOR,
|
|
475
|
-
avatarUrl: '/
|
|
475
|
+
avatarUrl: '/placeholder-user.jpg',
|
|
476
476
|
title: 'Registered',
|
|
477
477
|
groups: [
|
|
478
478
|
{ title: 'Registered', nameClass: null },
|