DebugDatabaseManager.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //
  2. // DebugDatabaseManager.m
  3. // YYDebugDatabase
  4. //
  5. // Created by wentian on 17/8/10.
  6. // Copyright © 2017年 wentian. All rights reserved.
  7. //
  8. #import "DebugDatabaseManager.h"
  9. #import "NSURL+scheme.h"
  10. #import "DatabaseUtil.h"
  11. #import "NSString+json.h"
  12. #ifdef COCOAPODS
  13. #import <GCDWebServer/GCDWebServerRequest.h>
  14. #import <GCDWebServer/GCDWebServerDataResponse.h>
  15. #else
  16. #import <GCDWebServers/GCDWebServerRequest.h>
  17. #import <GCDWebServers/GCDWebServerDataResponse.h>
  18. #endif
  19. @interface DebugDatabaseManager ()<GCDWebServerDelegate>
  20. @property(nonatomic, strong) NSDictionary *databasePaths;
  21. @end
  22. @implementation DebugDatabaseManager
  23. + (instancetype)shared {
  24. static dispatch_once_t oneceToken;
  25. static DebugDatabaseManager *debugDatabaseManager;
  26. dispatch_once(&oneceToken, ^{
  27. debugDatabaseManager = [[DebugDatabaseManager alloc] init];
  28. });
  29. return debugDatabaseManager;
  30. }
  31. - (id)init {
  32. self = [super init];
  33. if (self) {
  34. NSURL *bundleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"Web" withExtension:@"bundle"];
  35. NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
  36. [self addGETHandlerForBasePath:@"/" directoryPath:[bundle resourcePath] indexFilename:@"index.html" cacheAge:0 allowRangeRequests:YES];
  37. [self setupAdvanceRoutes];
  38. }
  39. return self;
  40. }
  41. - (void)setupAdvanceRoutes {
  42. __weak typeof(self)weakSelf = self;
  43. [self addHandlerForMethod:@"GET"
  44. path:@"/databaseList"
  45. requestClass:[GCDWebServerRequest class]
  46. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  47. return [GCDWebServerDataResponse responseWithJSONObject:@{@"rows" : weakSelf.databasePaths.allKeys ?: [NSNull null]}];
  48. }];
  49. [self addHandlerForMethod:@"GET"
  50. path:@"/tableList"
  51. requestClass:[GCDWebServerRequest class]
  52. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  53. NSDictionary *params = request.URL.queryParams;
  54. NSString *dbName = [params objectForKey:@"database"];
  55. [[DatabaseUtil shared] openDatabase:[weakSelf.databasePaths objectForKey:dbName]];
  56. NSArray *array = [[DatabaseUtil shared] allTables];
  57. [[DatabaseUtil shared] closeDatabase];
  58. return [GCDWebServerDataResponse responseWithJSONObject:@{@"rows" : array?:[NSNull null]}];
  59. }];
  60. [self addHandlerForMethod:@"GET"
  61. path:@"/allTableRecords"
  62. requestClass:[GCDWebServerRequest class]
  63. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  64. NSDictionary *params = request.URL.queryParams;
  65. NSString *dbName = [params objectForKey:@"database"];
  66. NSString *tableName = [params objectForKey:@"tableName"];
  67. [[DatabaseUtil shared] openDatabase:[weakSelf.databasePaths objectForKey:dbName]];
  68. NSDictionary *rows = [[DatabaseUtil shared] rowsInTable:tableName];
  69. [[DatabaseUtil shared] closeDatabase];
  70. return [GCDWebServerDataResponse responseWithJSONObject:rows?:[NSNull null]];
  71. }];
  72. [self addHandlerForMethod:@"GET"
  73. path:@"/updateRecord"
  74. requestClass:[GCDWebServerRequest class]
  75. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  76. NSDictionary *params = request.URL.queryParams;
  77. NSString *dbName = [params objectForKey:@"database"];
  78. NSString *tableName = [params objectForKey:@"tableName"];
  79. NSDictionary *updateData =[[yy_dicGetString(params, @"updatedData") URLDecode] JSONObject];
  80. BOOL isSuccess;
  81. if (updateData.count == 0 || dbName.length == 0 || tableName.length == 0) {
  82. isSuccess = NO;
  83. }else {
  84. NSMutableDictionary *contentValues = [NSMutableDictionary dictionary];
  85. NSMutableDictionary *where = [NSMutableDictionary dictionary];
  86. for (NSDictionary *columnDic in updateData) {
  87. if (yy_dicGetBool(columnDic, @"isPrimary", NO)) {
  88. [where setObject:[columnDic objectForKey:@"value"]?:[NSNull null] forKey:yy_dicGetStringSafe(columnDic, @"title")];
  89. } else {
  90. [contentValues setObject:[columnDic objectForKey:@"value"]?:[NSNull null] forKey:[columnDic objectForKey:@"title"]];
  91. }
  92. }
  93. [[DatabaseUtil shared] openDatabase:yy_dicGetString(weakSelf.databasePaths, dbName)];
  94. isSuccess = [[DatabaseUtil shared] updateRecordInDatabase:dbName tableName:tableName data:contentValues condition:where];
  95. [[DatabaseUtil shared] closeDatabase];
  96. }
  97. return [GCDWebServerDataResponse responseWithJSONObject:@{@"isSuccessful" : @(isSuccess)}];
  98. }];
  99. [self addHandlerForMethod:@"GET"
  100. path:@"/deleteRecord"
  101. requestClass:[GCDWebServerRequest class]
  102. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  103. NSDictionary *params = request.URL.queryParams;
  104. NSString *dbName = [params objectForKey:@"database"];
  105. NSString *tableName = [params objectForKey:@"tableName"];
  106. NSDictionary *deleteData =[[yy_dicGetString(params, @"deleteData") URLDecode] JSONObject];
  107. BOOL isSuccess;
  108. if (deleteData.count == 0 || dbName.length == 0 || tableName.length == 0) {
  109. isSuccess = NO;
  110. }else {
  111. NSMutableDictionary *where = [NSMutableDictionary dictionary];
  112. for (NSDictionary *columnDic in deleteData) {
  113. if (yy_dicGetBool(columnDic, @"isPrimary", NO)) {
  114. [where setObject:[columnDic objectForKey:@"value"]?:[NSNull null] forKey:yy_dicGetStringSafe(columnDic, @"title")];
  115. }
  116. }
  117. [[DatabaseUtil shared] openDatabase:yy_dicGetString(weakSelf.databasePaths, dbName)];
  118. isSuccess = [[DatabaseUtil shared] deleteRecordInDatabase:dbName tableName:tableName condition:where limit:nil];
  119. [[DatabaseUtil shared] closeDatabase];
  120. }
  121. return [GCDWebServerDataResponse responseWithJSONObject:@{@"isSuccessful" : @(isSuccess)}];
  122. }];
  123. [self addHandlerForMethod:@"GET"
  124. path:@"/query"
  125. requestClass:[GCDWebServerRequest class]
  126. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  127. NSDictionary *params = request.URL.queryParams;
  128. NSString *dbName = [params objectForKey:@"database"];
  129. NSString *query = [yy_dicGetString(params, @"query") URLDecode];
  130. NSString *tableName = [weakSelf getTableNameFromQuery:query];
  131. NSString *operator = [weakSelf getOperatorFromQuery:query];
  132. [[DatabaseUtil shared] openDatabase:yy_dicGetString(weakSelf.databasePaths, dbName)];
  133. NSDictionary *resultData = [[DatabaseUtil shared] executeQueryInDatabase:dbName tableName:tableName operator:operator query:query];
  134. [[DatabaseUtil shared] closeDatabase];
  135. return [GCDWebServerDataResponse responseWithJSONObject:resultData?:[NSNull null]];
  136. }];
  137. [self addHandlerForMethod:@"GET"
  138. path:@"/downloadDb"
  139. requestClass:[GCDWebServerRequest class]
  140. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  141. NSString *dbName = [request.URL.queryParams objectForKey:@"database"];
  142. return [GCDWebServerDataResponse responseWithData:[NSData dataWithContentsOfFile:yy_dicGetString(weakSelf.databasePaths, dbName)] contentType:@"application/octet-stream"];
  143. }];
  144. [self addHandlerForMethod:@"GET"
  145. path:@"/getUserDefault"
  146. requestClass:[GCDWebServerRequest class]
  147. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  148. NSMutableDictionary *userData = [[DatabaseUtil shared] userDefaultData].mutableCopy;
  149. [userData safe_setObject:@YES forKey:@"userDefault"];
  150. return [GCDWebServerDataResponse responseWithJSONObject:userData];
  151. }];
  152. [self addHandlerForMethod:@"GET"
  153. path:@"/getAppInfo"
  154. requestClass:[GCDWebServerRequest class]
  155. processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
  156. NSDictionary *appInfo = [[DatabaseUtil shared] getAppInfoData];
  157. return [GCDWebServerDataResponse responseWithJSONObject:appInfo];
  158. }];
  159. }
  160. - (void)startServerOnPort:(NSInteger)port directories:(NSArray *)directories {
  161. _databasePaths = [self getAllDBPathsWithDirectories:directories];
  162. [self startWithPort:port bonjourName:@""];
  163. NSLog(@"Visit %@ in your web browser", self.serverURL);
  164. }
  165. - (void)startServerOnPort:(NSInteger)port {
  166. NSString *cacheDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];
  167. NSString *documentDir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
  168. [self startServerOnPort:port directories:@[cacheDir, documentDir]];
  169. }
  170. - (NSDictionary*)getAllDBPathsWithDirectories:(NSArray*)directories {
  171. NSMutableDictionary *paths = @{}.mutableCopy;
  172. for (NSString *directory in directories) {
  173. NSArray *dirList = [[[NSFileManager defaultManager] subpathsAtPath:directory] pathsMatchingExtensions:[self supportedDatabaseSuffixs]];
  174. for (NSString *subPath in dirList) {
  175. if ([self checkDatabaseFile:subPath]) {
  176. // [paths setObject:[directory stringByAppendingPathComponent:subPath] forKey:subPath.lastPathComponent];
  177. // added by seven
  178. [self addToDbQueryPathesSupportDuplicatedFilename:paths
  179. object:[directory stringByAppendingPathComponent:subPath]
  180. key:subPath.lastPathComponent];
  181. }
  182. }
  183. if ([self checkDatabaseFile:directory]) {
  184. // [paths setObject:directory forKey:directory.lastPathComponent];
  185. // added by seven
  186. [self addToDbQueryPathesSupportDuplicatedFilename:paths
  187. object:directory
  188. key:directory.lastPathComponent];
  189. }
  190. }
  191. return paths;
  192. }
  193. // added by seven
  194. - (void)addToDbQueryPathesSupportDuplicatedFilename:(NSMutableDictionary *)paths object:(NSString *)dbPath key:(NSString *)dbKeyName {
  195. // Key exists, appending the last directory path component
  196. if ([paths objectForKey:dbKeyName]) {
  197. NSString *dbDir = [dbPath stringByDeletingLastPathComponent];
  198. NSArray *dbDirPathComponents = [dbDir componentsSeparatedByString:@"/"];
  199. NSString *lastPathComponent = [dbDirPathComponents lastObject];
  200. if (lastPathComponent && lastPathComponent.length) {
  201. dbKeyName = [NSString stringWithFormat:@"%@/%@", lastPathComponent, dbKeyName];
  202. }
  203. }
  204. [paths setObject:dbPath forKey:dbKeyName];
  205. }
  206. - (BOOL)checkDatabaseFile:(NSString*)fileName {
  207. for (NSString *suffix in [self supportedDatabaseSuffixs]) {
  208. if ([fileName hasSuffix:suffix]) {
  209. return YES;
  210. }
  211. }
  212. return NO;
  213. }
  214. - (NSArray*)supportedDatabaseSuffixs {
  215. return @[@"sqlite", @"SQLITE", @"db", @"DB", @"sqlite3", @"SQLITE3"];
  216. }
  217. - (NSString*)getTableNameFromQuery:(NSString*)query {
  218. NSArray *words = [query componentsSeparatedByString:@" "];
  219. NSString *operator = [[words firstObject] lowercaseString];
  220. NSInteger fromIndex = 0;
  221. NSString *table;
  222. for (int i =0;i<[words count];i++) {
  223. NSString *word = [words objectAtIndex:i];
  224. if ([operator isEqualToString:@"select"] || [operator isEqualToString:@"delete"]) {
  225. if ([word isEqualToString:@"from"]) {
  226. fromIndex = i;
  227. }
  228. if (i == fromIndex+1) {
  229. if([word stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length==0){
  230. fromIndex ++;
  231. }else{
  232. table = word;
  233. }
  234. }
  235. }else if ([operator isEqualToString:@"update"]) {
  236. if ([word isEqualToString:@"update"]) {
  237. fromIndex = i;
  238. }
  239. if (i == fromIndex+1) {
  240. if([word stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length==0){
  241. fromIndex ++;
  242. }else{
  243. table = word;
  244. }
  245. }
  246. }
  247. }
  248. return table;
  249. }
  250. - (NSString*)getOperatorFromQuery:(NSString*)query {
  251. NSArray *words = [query componentsSeparatedByString:@" "];
  252. NSString *operator = [[words firstObject] lowercaseString];
  253. return operator;
  254. }
  255. @end