1+ import assert from "node:assert" ;
2+ import { randomBytes } from "node:crypto" ;
13import fs from "node:fs" ;
24import path from "node:path" ;
35
@@ -11,26 +13,43 @@ import * as EnterpriseUtil from "../../../common/enterprise-util.ts";
1113import Logger from "../../../common/logger-util.ts" ;
1214import * as Messages from "../../../common/messages.ts" ;
1315import * as t from "../../../common/translation-util.ts" ;
14- import type { ServerConfig } from "../../../common/types.ts" ;
16+ import type { ServerConfig , ServerSettings } from "../../../common/types.ts" ;
1517import defaultIcon from "../../img/icon.png" ;
1618import { ipcRenderer } from "../typed-ipc-renderer.ts" ;
1719
1820const logger = new Logger ( {
1921 file : "domain-util.log" ,
2022} ) ;
2123
24+ export function generateDomainId ( ) : string {
25+ return randomBytes ( 5 ) . toString ( "hex" ) ;
26+ }
27+
2228// For historical reasons, we store this string in domain.json to denote a
2329// missing icon; it does not change with the actual icon location.
2430export const defaultIconSentinel = "../renderer/img/icon.png" ;
2531
26- const serverConfigSchema = z . object ( {
32+ const storedServerSchema = z . object ( {
33+ id : z . string ( ) . optional ( ) ,
2734 url : z . url ( ) ,
2835 alias : z . string ( ) ,
2936 icon : z . string ( ) ,
3037 zulipVersion : z . string ( ) . default ( "unknown" ) ,
3138 zulipFeatureLevel : z . number ( ) . default ( 0 ) ,
3239} ) ;
3340
41+ const serverConfigSchema = storedServerSchema . extend ( {
42+ id : z . string ( ) ,
43+ } ) ;
44+
45+ function addServerId ( server : z . infer < typeof storedServerSchema > ) : ServerConfig {
46+ assert . ok ( server . id === undefined ) ;
47+ return serverConfigSchema . parse ( {
48+ ...server ,
49+ id : generateDomainId ( ) ,
50+ } ) ;
51+ }
52+
3453let database ! : JsonDB ;
3554
3655reloadDatabase ( ) ;
@@ -88,8 +107,8 @@ export async function addDomain(server: {
88107 server . icon = defaultIconSentinel ;
89108 }
90109
91- serverConfigSchema . parse ( server ) ;
92- database . push ( "/domains[]" , server , true ) ;
110+ const serverWithId = addServerId ( storedServerSchema . parse ( server ) ) ;
111+ database . push ( "/domains[]" , serverWithId , true ) ;
93112 reloadDatabase ( ) ;
94113}
95114
@@ -117,7 +136,7 @@ export function duplicateDomain(domain: string): boolean {
117136export async function checkDomain (
118137 domain : string ,
119138 silent = false ,
120- ) : Promise < ServerConfig > {
139+ ) : Promise < ServerSettings > {
121140 if ( ! silent && duplicateDomain ( domain ) ) {
122141 // Do not check duplicate in silent mode
123142 throw new Error ( "This server has been added." ) ;
@@ -126,13 +145,13 @@ export async function checkDomain(
126145 domain = formatUrl ( domain ) ;
127146
128147 try {
129- return await getServerSettings ( domain ) ;
148+ return storedServerSchema . parse ( await getServerSettings ( domain ) ) ;
130149 } catch {
131150 throw new Error ( Messages . invalidZulipServerError ( domain ) ) ;
132151 }
133152}
134153
135- async function getServerSettings ( domain : string ) : Promise < ServerConfig > {
154+ async function getServerSettings ( domain : string ) : Promise < ServerSettings > {
136155 return ipcRenderer . invoke ( "get-server-settings" , domain ) ;
137156}
138157
@@ -151,7 +170,11 @@ export async function updateSavedServer(
151170 const serverConfig = getDomain ( index ) ;
152171 const oldIcon = serverConfig . icon ;
153172 try {
154- const newServerConfig = await checkDomain ( url , true ) ;
173+ const newServerSetting = await checkDomain ( url , true ) ;
174+ const newServerConfig : ServerConfig = {
175+ ...newServerSetting ,
176+ id : serverConfig . id ,
177+ } ;
155178 const localIconUrl = await saveServerIcon ( newServerConfig . icon ) ;
156179 if ( ! oldIcon || localIconUrl !== defaultIconSentinel ) {
157180 newServerConfig . icon = localIconUrl ;
@@ -168,6 +191,31 @@ export async function updateSavedServer(
168191 }
169192}
170193
194+ function ensureDomainIds ( ) : void {
195+ try {
196+ const domains = storedServerSchema
197+ . array ( )
198+ . parse ( database . getObject < unknown > ( "/domains" ) ) ;
199+
200+ let changed = false ;
201+
202+ const updatedDomains = domains . map ( ( server ) => {
203+ if ( server . id === undefined ) {
204+ changed = true ;
205+ server = addServerId ( server ) ;
206+ }
207+
208+ return server ;
209+ } ) ;
210+
211+ if ( changed ) {
212+ database . push ( "/domains" , updatedDomains , true ) ;
213+ }
214+ } catch ( error : unknown ) {
215+ if ( ! ( error instanceof DataError ) ) throw error ;
216+ }
217+ }
218+
171219function reloadDatabase ( ) : void {
172220 const domainJsonPath = path . join (
173221 app . getPath ( "userData" ) ,
@@ -192,6 +240,7 @@ function reloadDatabase(): void {
192240 }
193241
194242 database = new JsonDB ( domainJsonPath , true , true ) ;
243+ ensureDomainIds ( ) ;
195244}
196245
197246export function formatUrl ( domain : string ) : string {
0 commit comments