summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages/admin/settings.vue
blob: 541ee7c0cd18098afbd2d0209ca05d3ee39cedd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<PageWithHeader :tabs="headerTabs">
	<div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;">
		<SearchMarker path="/admin/settings" :label="i18n.ts.general" :keywords="['general', 'settings']" icon="ti ti-settings">
			<div class="_gaps_m">
				<SearchMarker v-slot="slotProps" :keywords="['information', 'meta']">
					<MkFolder :defaultOpen="true">
						<template #icon><SearchIcon><i class="ti ti-info-circle"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts.info }}</SearchLabel></template>
						<template v-if="infoForm.modified.value" #footer>
							<MkFormFooter :form="infoForm"/>
						</template>

						<div class="_gaps">
							<SearchMarker :keywords="['name']">
								<MkInput v-model="infoForm.state.name">
									<template #label><SearchLabel>{{ i18n.ts.instanceName }}</SearchLabel><span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template>
								</MkInput>
							</SearchMarker>

							<SearchMarker :keywords="['shortName']">
								<MkInput v-model="infoForm.state.shortName">
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.shortName }}</SearchLabel> ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts._serverSettings.shortNameDescription }}</SearchText></template>
								</MkInput>
							</SearchMarker>

							<SearchMarker :keywords="['description']">
								<MkTextarea v-model="infoForm.state.description">
									<template #label><SearchLabel>{{ i18n.ts.instanceDescription }}</SearchLabel><span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template>
								</MkTextarea>
							</SearchMarker>

							<FormSplit :minWidth="300">
								<SearchMarker :keywords="['maintainer', 'name']">
									<MkInput v-model="infoForm.state.maintainerName">
										<template #label><SearchLabel>{{ i18n.ts.maintainerName }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template>
									</MkInput>
								</SearchMarker>

								<SearchMarker :keywords="['maintainer', 'email', 'contact']">
									<MkInput v-model="infoForm.state.maintainerEmail" type="email">
										<template #label><SearchLabel>{{ i18n.ts.maintainerEmail }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #prefix><i class="ti ti-mail"></i></template>
									</MkInput>
								</SearchMarker>
							</FormSplit>

							<SearchMarker :keywords="['tos', 'termsOfService']">
								<MkInput v-model="infoForm.state.tosUrl" type="url">
									<template #label><SearchLabel>{{ i18n.ts.tosUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #prefix><i class="ti ti-link"></i></template>
								</MkInput>
							</SearchMarker>

							<SearchMarker :keywords="['privacyPolicy']">
								<MkInput v-model="infoForm.state.privacyPolicyUrl" type="url">
									<template #label><SearchLabel>{{ i18n.ts.privacyPolicyUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #prefix><i class="ti ti-link"></i></template>
								</MkInput>
							</SearchMarker>

							<SearchMarker :keywords="['inquiry', 'contact']">
								<MkInput v-model="infoForm.state.inquiryUrl" type="url">
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.inquiryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</SearchText></template>
									<template #prefix><i class="ti ti-link"></i></template>
								</MkInput>
							</SearchMarker>

							<SearchMarker :keywords="['repository', 'url']">
								<MkInput v-model="infoForm.state.repositoryUrl" type="url">
									<template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts.repositoryUrlDescription }}</SearchText></template>
									<template #prefix><i class="ti ti-link"></i></template>
								</MkInput>
							</SearchMarker>

							<MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn>
								{{ i18n.ts.repositoryUrlOrTarballRequired }}
							</MkInfo>

							<SearchMarker :keywords="['impressum', 'legalNotice']">
								<MkInput v-model="infoForm.state.impressumUrl" type="url">
									<template #label><SearchLabel>{{ i18n.ts.impressumUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts.impressumDescription }}</SearchText></template>
									<template #prefix><i class="ti ti-link"></i></template>
								</MkInput>
							</SearchMarker>
						</div>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['pinned', 'users']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-user-star"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts.pinnedUsers }}</SearchLabel></template>
						<template v-if="pinnedUsersForm.modified.value" #footer>
							<MkFormFooter :form="pinnedUsersForm"/>
						</template>

						<MkTextarea v-model="pinnedUsersForm.state.pinnedUsers">
							<template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template>
							<template #caption><SearchText>{{ i18n.ts.pinnedUsersDescription }}</SearchText></template>
						</MkTextarea>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['serviceWorker']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-world-cog"></i></SearchIcon></template>
						<template #label><SearchLabel>ServiceWorker</SearchLabel></template>
						<template v-if="serviceWorkerForm.modified.value" #footer>
							<MkFormFooter :form="serviceWorkerForm"/>
						</template>

						<div class="_gaps">
							<SearchMarker>
								<MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker">
									<template #label><SearchLabel>{{ i18n.ts.enableServiceworker }}</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts.serviceworkerInfo }}</SearchText></template>
								</MkSwitch>
							</SearchMarker>

							<template v-if="serviceWorkerForm.state.enableServiceWorker">
								<SearchMarker>
									<MkInput v-model="serviceWorkerForm.state.swPublicKey">
										<template #label><SearchLabel>Public key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #prefix><i class="ti ti-key"></i></template>
									</MkInput>
								</SearchMarker>

								<SearchMarker>
									<MkInput v-model="serviceWorkerForm.state.swPrivateKey">
										<template #label><SearchLabel>Private key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #prefix><i class="ti ti-key"></i></template>
									</MkInput>
								</SearchMarker>
							</template>
						</div>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['ads']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-ad"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts._ad.adsSettings }}</SearchLabel></template>
						<template v-if="adForm.modified.value" #footer>
							<MkFormFooter :form="adForm"/>
						</template>

						<div class="_gaps">
							<div class="_gaps_s">
								<SearchMarker>
									<MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number">
										<template #label><SearchLabel>{{ i18n.ts._ad.notesPerOneAd }}</SearchLabel><span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template>
									</MkInput>
								</SearchMarker>

								<MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true">
									{{ i18n.ts._ad.adsTooClose }}
								</MkInfo>
							</div>
						</div>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['url', 'preview']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-world-search"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.title }}</SearchLabel></template>
						<template v-if="urlPreviewForm.modified.value" #footer>
							<MkFormFooter :form="urlPreviewForm"/>
						</template>

						<div class="_gaps">
							<SearchMarker>
								<MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled">
									<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.enable }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template>
								</MkSwitch>
							</SearchMarker>

							<template v-if="urlPreviewForm.state.urlPreviewEnabled">
								<SearchMarker :keywords="['allow', 'redirect']">
									<MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect">
										<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.allowRedirect }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template>
									</MkSwitch>
								</SearchMarker>

								<SearchMarker :keywords="['contentLength']">
									<MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength">
										<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
									</MkSwitch>
								</SearchMarker>

								<SearchMarker :keywords="['contentLength']">
									<MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number">
										<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
									</MkInput>
								</SearchMarker>

								<SearchMarker :keywords="['timeout']">
									<MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number">
										<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.timeout }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
									</MkInput>
								</SearchMarker>

								<SearchMarker :keywords="['userAgent']">
									<MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text">
										<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.userAgent }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
									</MkInput>
								</SearchMarker>

								<div>
									<SearchMarker :keywords="['proxy']">
										<MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text">
											<template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template>
											<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
										</MkInput>
									</SearchMarker>

									<div :class="$style.subCaption">
										{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
										<ul style="padding-left: 20px; margin: 4px 0">
											<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
											<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
											<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
											<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
										</ul>
									</div>
								</div>
							</template>
						</div>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['federation']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-planet"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts.federation }}</SearchLabel></template>
						<template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template>
						<template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template>
						<template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template>
						<template v-if="federationForm.modified.value" #footer>
							<MkFormFooter :form="federationForm"/>
						</template>

						<div class="_gaps">
							<SearchMarker>
								<MkRadios v-model="federationForm.state.federation">
									<template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
									<option value="all">{{ i18n.ts.all }}</option>
									<option value="specified">{{ i18n.ts.specifyHost }}</option>
									<option value="none">{{ i18n.ts.none }}</option>
								</MkRadios>
							</SearchMarker>

							<SearchMarker :keywords="['hosts']">
								<MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts">
									<template #label><SearchLabel>{{ i18n.ts.federationAllowedHosts }}</SearchLabel><span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template>
								</MkTextarea>
							</SearchMarker>

							<SearchMarker :keywords="['suspended', 'software']">
								<MkFolder>
									<template #icon><i class="ti ti-list"></i></template>
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template>
									<template #footer>
										<div class="_buttons">
											<MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
										</div>
									</template>

									<div :class="$style.metadataRoot" class="_gaps_s">
										<MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo>
										<div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem">
											<button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button>
											<div :class="$style.dragItemForm">
												<FormSplit :minWidth="200">
													<MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName">
													</MkInput>
													<MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version">
													</MkInput>
												</FormSplit>
											</div>
										</div>
									</div>
								</MkFolder>
							</SearchMarker>

							<SearchMarker :keywords="['sign', 'get']">
								<MkSwitch v-model="federationForm.state.signToActivityPubGet">
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.signToActivityPubGet }}</SearchLabel><span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</SearchText></template>
								</MkSwitch>
							</SearchMarker>

							<SearchMarker :keywords="['proxy', 'remote', 'files']">
								<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.proxyRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</SearchText></template>
								</MkSwitch>
							</SearchMarker>

							<SearchMarker :keywords="['allow', 'external', 'redirect']">
								<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
									<template #label><SearchLabel>{{ i18n.ts._serverSettings.allowExternalApRedirect }}</SearchLabel><span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption>
										<div><SearchText>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</SearchText></div>
										<div>{{ i18n.ts.needToRestartServerToApply }}</div>
									</template>
								</MkSwitch>
							</SearchMarker>

							<SearchMarker :keywords="['cache', 'remote', 'files']">
								<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
									<template #label><SearchLabel>{{ i18n.ts.cacheRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
									<template #caption><SearchText>{{ i18n.ts.cacheRemoteFilesDescription }}</SearchText>{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
								</MkSwitch>
							</SearchMarker>

							<template v-if="federationForm.state.cacheRemoteFiles">
								<SearchMarker :keywords="['cache', 'remote', 'sensitive', 'files']">
									<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
										<template #label><SearchLabel>{{ i18n.ts.cacheRemoteSensitiveFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
										<template #caption><SearchText>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</SearchText></template>
									</MkSwitch>
								</SearchMarker>
							</template>
						</div>
					</MkFolder>
				</SearchMarker>

				<SearchMarker v-slot="slotProps" :keywords="['proxy', 'account']">
					<MkFolder :defaultOpen="slotProps.isParentOfTarget">
						<template #icon><SearchIcon><i class="ti ti-ghost"></i></SearchIcon></template>
						<template #label><SearchLabel>{{ i18n.ts.proxyAccount }}</SearchLabel></template>
						<template v-if="proxyAccountForm.modified.value" #footer>
							<MkFormFooter :form="proxyAccountForm"/>
						</template>

						<div class="_gaps">
							<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>

							<SearchMarker :keywords="['description']">
								<MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true">
									<template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template>
									<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
								</MkTextarea>
							</SearchMarker>
						</div>
					</MkFolder>
				</SearchMarker>

				<MkButton primary @click="openSetupWizard">
					Open setup wizard
				</MkButton>
			</div>
		</SearchMarker>
	</div>
</PageWithHeader>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import { useForm } from '@/composables/use-form.js';
import MkFormFooter from '@/components/MkFormFooter.vue';
import MkRadios from '@/components/MkRadios.vue';

const meta = await misskeyApi('admin/meta');

const proxyAccount = await misskeyApi('users/show', { userId: meta.proxyAccountId });

const infoForm = useForm({
	name: meta.name ?? '',
	shortName: meta.shortName ?? '',
	description: meta.description ?? '',
	maintainerName: meta.maintainerName ?? '',
	maintainerEmail: meta.maintainerEmail ?? '',
	tosUrl: meta.tosUrl ?? '',
	privacyPolicyUrl: meta.privacyPolicyUrl ?? '',
	inquiryUrl: meta.inquiryUrl ?? '',
	repositoryUrl: meta.repositoryUrl ?? '',
	impressumUrl: meta.impressumUrl ?? '',
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		name: state.name,
		shortName: state.shortName === '' ? null : state.shortName,
		description: state.description,
		maintainerName: state.maintainerName,
		maintainerEmail: state.maintainerEmail,
		tosUrl: state.tosUrl,
		privacyPolicyUrl: state.privacyPolicyUrl,
		inquiryUrl: state.inquiryUrl,
		repositoryUrl: state.repositoryUrl,
		impressumUrl: state.impressumUrl,
	});
	fetchInstance(true);
});

const pinnedUsersForm = useForm({
	pinnedUsers: meta.pinnedUsers.join('\n'),
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		pinnedUsers: state.pinnedUsers.split('\n'),
	});
	fetchInstance(true);
});

const serviceWorkerForm = useForm({
	enableServiceWorker: meta.enableServiceWorker,
	swPublicKey: meta.swPublickey ?? '',
	swPrivateKey: meta.swPrivateKey ?? '',
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		enableServiceWorker: state.enableServiceWorker,
		swPublicKey: state.swPublicKey,
		swPrivateKey: state.swPrivateKey,
	});
	fetchInstance(true);
});

const adForm = useForm({
	notesPerOneAd: meta.notesPerOneAd,
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		notesPerOneAd: state.notesPerOneAd,
	});
	fetchInstance(true);
});

const urlPreviewForm = useForm({
	urlPreviewEnabled: meta.urlPreviewEnabled,
	urlPreviewAllowRedirect: meta.urlPreviewAllowRedirect,
	urlPreviewTimeout: meta.urlPreviewTimeout,
	urlPreviewMaximumContentLength: meta.urlPreviewMaximumContentLength,
	urlPreviewRequireContentLength: meta.urlPreviewRequireContentLength,
	urlPreviewUserAgent: meta.urlPreviewUserAgent ?? '',
	urlPreviewSummaryProxyUrl: meta.urlPreviewSummaryProxyUrl ?? '',
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		urlPreviewEnabled: state.urlPreviewEnabled,
		urlPreviewAllowRedirect: state.urlPreviewAllowRedirect,
		urlPreviewTimeout: state.urlPreviewTimeout,
		urlPreviewMaximumContentLength: state.urlPreviewMaximumContentLength,
		urlPreviewRequireContentLength: state.urlPreviewRequireContentLength,
		urlPreviewUserAgent: state.urlPreviewUserAgent,
		urlPreviewSummaryProxyUrl: state.urlPreviewSummaryProxyUrl,
	});
	fetchInstance(true);
});

const federationForm = useForm({
	federation: meta.federation,
	federationHosts: meta.federationHosts.join('\n'),
	deliverSuspendedSoftware: meta.deliverSuspendedSoftware,
	signToActivityPubGet: meta.signToActivityPubGet,
	proxyRemoteFiles: meta.proxyRemoteFiles,
	allowExternalApRedirect: meta.allowExternalApRedirect,
	cacheRemoteFiles: meta.cacheRemoteFiles,
	cacheRemoteSensitiveFiles: meta.cacheRemoteSensitiveFiles,
}, async (state) => {
	await os.apiWithDialog('admin/update-meta', {
		federation: state.federation,
		federationHosts: state.federationHosts.split('\n'),
		deliverSuspendedSoftware: state.deliverSuspendedSoftware,
		signToActivityPubGet: state.signToActivityPubGet,
		proxyRemoteFiles: state.proxyRemoteFiles,
		allowExternalApRedirect: state.allowExternalApRedirect,
		cacheRemoteFiles: state.cacheRemoteFiles,
		cacheRemoteSensitiveFiles: state.cacheRemoteSensitiveFiles,
	});
	fetchInstance(true);
});

const proxyAccountForm = useForm({
	description: proxyAccount.description,
}, async (state) => {
	await os.apiWithDialog('admin/update-proxy-account', {
		description: state.description,
	});
	fetchInstance(true);
});

async function openSetupWizard() {
	const { canceled } = await os.confirm({
		type: 'warning',
		title: i18n.ts._serverSettings.restartServerSetupWizardConfirm_title,
		text: i18n.ts._serverSettings.restartServerSetupWizardConfirm_text,
	});
	if (canceled) return;

	const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkServerSetupWizardDialog.vue').then(x => x.default), {
	}, {
		closed: () => dispose(),
	});
}

const headerTabs = computed(() => []);

definePage(() => ({
	title: i18n.ts.general,
	icon: 'ti ti-settings',
}));
</script>

<style lang="scss" module>
.subCaption {
	font-size: 0.85em;
	color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
}

.metadataRoot {
	container-type: inline-size;
}

.fieldDragItem {
	display: flex;
	padding: 10px;
	align-items: flex-end;
	border-radius: 6px;

	/* (drag button) 32px + (drag button margin) 8px + (input width) 200px * 2 + (input gap) 12px = 452px */
	@container (max-width: 452px) {
		align-items: center;
	}
}

.dragItemHandle {
	cursor: grab;
	width: 32px;
	height: 32px;
	margin: 0 8px 0 0;
	opacity: 0.5;
	flex-shrink: 0;

	&:active {
		cursor: grabbing;
	}
}

.dragItemRemove {
	@extend .dragItemHandle;

	color: #ff2a2a;
	opacity: 1;
	cursor: pointer;

	&:hover, &:focus {
		opacity: .7;
	}

	&:active {
		cursor: pointer;
	}
}

.dragItemForm {
	flex-grow: 1;
}
</style>