1
1
import fs from 'fs' ;
2
2
import { promisify } from 'util' ;
3
3
import path from 'path' ;
4
+ import os from 'os' ;
4
5
import { IChallenge , IUser } from '../types' ;
5
6
6
7
const writeFile = promisify ( fs . writeFile ) ;
7
8
8
- export async function saveUserState ( state : IUser ) {
9
- const configPath = path . join ( process . cwd ( ) , "storage" ) ;
9
+ export function getConfigPath ( ) : string {
10
+ return path . join ( os . homedir ( ) , '.eth-tech-tree' , 'config' ) ;
11
+ }
12
+
13
+ function getLegacyConfigPath ( ) : string {
14
+ return path . join ( process . cwd ( ) , "storage" ) ;
15
+ }
16
+
17
+ function ensureConfigExists ( ) : void {
18
+ const configPath = getConfigPath ( ) ;
10
19
if ( ! fs . existsSync ( configPath ) ) {
11
- fs . mkdirSync ( configPath ) ;
20
+ fs . mkdirSync ( configPath , { recursive : true } ) ;
12
21
}
13
- const filePath = path . join ( configPath , "user.json" ) ;
14
- await writeFile ( filePath , JSON . stringify ( state , null , 2 ) ) ;
15
22
}
16
23
17
- export function loadUserState ( ) : IUser {
24
+ function migrateLegacyConfig ( ) : void {
25
+ const legacyPath = getLegacyConfigPath ( ) ;
26
+ const newPath = getConfigPath ( ) ;
27
+
28
+ if ( fs . existsSync ( legacyPath ) ) {
29
+ ensureConfigExists ( ) ;
30
+
31
+ let migratedAnyFiles = false ;
32
+
33
+ // Migrate user.json if it exists
34
+ const legacyUserFile = path . join ( legacyPath , "user.json" ) ;
35
+ const newUserFile = path . join ( newPath , "user.json" ) ;
36
+ if ( fs . existsSync ( legacyUserFile ) && ! fs . existsSync ( newUserFile ) ) {
37
+ fs . copyFileSync ( legacyUserFile , newUserFile ) ;
38
+ fs . unlinkSync ( legacyUserFile ) ;
39
+ console . log ( "Migrated user config from local storage to home directory" ) ;
40
+ migratedAnyFiles = true ;
41
+ }
42
+
43
+ // Migrate challenges.json if it exists
44
+ const legacyChallengesFile = path . join ( legacyPath , "challenges.json" ) ;
45
+ const newChallengesFile = path . join ( newPath , "challenges.json" ) ;
46
+ if ( fs . existsSync ( legacyChallengesFile ) && ! fs . existsSync ( newChallengesFile ) ) {
47
+ fs . copyFileSync ( legacyChallengesFile , newChallengesFile ) ;
48
+ fs . unlinkSync ( legacyChallengesFile ) ;
49
+ console . log ( "Migrated challenges config from local storage to home directory" ) ;
50
+ migratedAnyFiles = true ;
51
+ }
52
+
53
+ // Remove the legacy directory if it's empty after migration
54
+ if ( migratedAnyFiles ) {
55
+ try {
56
+ const remainingFiles = fs . readdirSync ( legacyPath ) ;
57
+ if ( remainingFiles . length === 0 ) {
58
+ fs . rmdirSync ( legacyPath ) ;
59
+ console . log ( "Removed legacy storage directory" ) ;
60
+ }
61
+ } catch ( error ) {
62
+ // Ignore errors when trying to remove the directory
63
+ // (it might not be empty or we might not have permissions)
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ function loadConfigWithMigration < T > ( filename : string , defaultValue : T ) : T {
18
70
try {
19
- const configPath = path . join ( process . cwd ( ) , "storage" , `user.json` ) ;
20
- const data = fs . readFileSync ( configPath , 'utf8' ) ;
21
- return JSON . parse ( data ) as IUser ;
71
+ const filePath = path . join ( getConfigPath ( ) , filename ) ;
72
+ const data = fs . readFileSync ( filePath , 'utf8' ) ;
73
+ return JSON . parse ( data ) ;
22
74
} catch ( error : any ) {
23
75
if ( error . code === 'ENOENT' ) {
24
- return { } as IUser ; // Return empty object if file doesn't exist
76
+ migrateLegacyConfig ( ) ;
77
+
78
+ try {
79
+ const filePath = path . join ( getConfigPath ( ) , filename ) ;
80
+ const data = fs . readFileSync ( filePath , 'utf8' ) ;
81
+ return JSON . parse ( data ) ;
82
+ } catch ( migrationError : any ) {
83
+ if ( migrationError . code === 'ENOENT' ) {
84
+ return defaultValue ; // Return default value if file doesn't exist even after migration
85
+ }
86
+ throw migrationError ;
87
+ }
25
88
}
26
89
throw error ;
27
90
}
28
91
}
29
92
30
- export async function saveChallenges ( challenges : IChallenge [ ] ) {
31
- const configPath = path . join ( process . cwd ( ) , "storage" ) ;
93
+ export async function saveUserState ( state : IUser ) {
94
+ ensureConfigExists ( ) ;
95
+ const filePath = path . join ( getConfigPath ( ) , "user.json" ) ;
96
+ await writeFile ( filePath , JSON . stringify ( state , null , 2 ) ) ;
97
+ }
32
98
33
- if ( ! fs . existsSync ( configPath ) ) {
34
- fs . mkdirSync ( configPath ) ;
35
- }
36
- const filePath = path . join ( configPath , "challenges.json" ) ;
99
+ export function loadUserState ( ) : IUser {
100
+ return loadConfigWithMigration ( "user.json" , { } as IUser ) ;
101
+ }
102
+
103
+ export async function saveChallenges ( challenges : IChallenge [ ] ) {
104
+ ensureConfigExists ( ) ;
105
+ const filePath = path . join ( getConfigPath ( ) , "challenges.json" ) ;
37
106
await writeFile ( filePath , JSON . stringify ( challenges , null , 2 ) ) ;
38
107
}
39
108
40
109
export function loadChallenges ( ) : IChallenge [ ] {
41
- try {
42
- const configPath = path . join ( process . cwd ( ) , "storage" , `challenges.json` ) ;
43
- const data = fs . readFileSync ( configPath , 'utf8' ) ;
44
- return JSON . parse ( data ) ;
45
- } catch ( error : any ) {
46
- if ( error . code === 'ENOENT' ) {
47
- return [ ] ; // Return empty array if file doesn't exist
48
- }
49
- throw error ;
50
- }
51
- }
110
+ return loadConfigWithMigration ( "challenges.json" , [ ] ) ;
111
+ }
0 commit comments