kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add Storage Service plugin to Spinner.
rodzic
9bc25132c3
commit
89a6730efe
|
@ -69,6 +69,9 @@ class SpinnerApplicationContext : ApplicationContext() {
|
||||||
"megaphones" to DatabaseConfig(db = MegaphoneDatabase.getInstance(this).sqlCipherDatabase),
|
"megaphones" to DatabaseConfig(db = MegaphoneDatabase.getInstance(this).sqlCipherDatabase),
|
||||||
"localmetrics" to DatabaseConfig(db = LocalMetricsDatabase.getInstance(this).sqlCipherDatabase),
|
"localmetrics" to DatabaseConfig(db = LocalMetricsDatabase.getInstance(this).sqlCipherDatabase),
|
||||||
"logs" to DatabaseConfig(db = LogDatabase.getInstance(this).sqlCipherDatabase),
|
"logs" to DatabaseConfig(db = LogDatabase.getInstance(this).sqlCipherDatabase),
|
||||||
|
),
|
||||||
|
linkedMapOf(
|
||||||
|
StorageServicePlugin.PATH to StorageServicePlugin()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.thoughtcrime.securesms
|
||||||
|
|
||||||
|
import org.signal.spinner.Plugin
|
||||||
|
import org.signal.spinner.PluginResult
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
|
||||||
|
class StorageServicePlugin : Plugin {
|
||||||
|
override val name: String = "Storage"
|
||||||
|
override val path: String = PATH
|
||||||
|
|
||||||
|
override fun get(): PluginResult {
|
||||||
|
val columns = listOf("Type", "Data")
|
||||||
|
val rows = mutableListOf<List<String>>()
|
||||||
|
|
||||||
|
val manager = ApplicationDependencies.getSignalServiceAccountManager()
|
||||||
|
val storageServiceKey = SignalStore.storageService().orCreateStorageKey
|
||||||
|
val storageManifestVersion = manager.storageManifestVersion
|
||||||
|
val manifest = manager.getStorageManifestIfDifferentVersion(storageServiceKey, storageManifestVersion - 1).get()
|
||||||
|
val signalStorageRecords = manager.readStorageRecords(storageServiceKey, manifest.storageIds)
|
||||||
|
|
||||||
|
for (record in signalStorageRecords) {
|
||||||
|
val row = mutableListOf<String>()
|
||||||
|
|
||||||
|
if (record.account.isPresent) {
|
||||||
|
row += "Account"
|
||||||
|
row += record.account.get().toProto().toString()
|
||||||
|
} else if (record.contact.isPresent) {
|
||||||
|
row += "Contact"
|
||||||
|
row += record.contact.get().toProto().toString()
|
||||||
|
} else if (record.groupV1.isPresent) {
|
||||||
|
row += "GV1"
|
||||||
|
row += record.groupV1.get().toProto().toString()
|
||||||
|
} else if (record.groupV2.isPresent) {
|
||||||
|
row += "GV2"
|
||||||
|
row += record.groupV2.get().toProto().toString()
|
||||||
|
} else if (record.storyDistributionList.isPresent) {
|
||||||
|
row += "Distribution List"
|
||||||
|
row += record.storyDistributionList.get().toProto().toString()
|
||||||
|
} else {
|
||||||
|
row += "Unknown"
|
||||||
|
row += ""
|
||||||
|
}
|
||||||
|
rows += row
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.sortBy { it.first() }
|
||||||
|
|
||||||
|
return PluginResult.TableResult(
|
||||||
|
columns = columns,
|
||||||
|
rows = rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PATH = "/storage"
|
||||||
|
}
|
||||||
|
}
|
|
@ -268,7 +268,7 @@ public final class SignalAccountRecord implements SignalRecord {
|
||||||
return proto.getSubscriptionManuallyCancelled();
|
return proto.getSubscriptionManuallyCancelled();
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountRecord toProto() {
|
public AccountRecord toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ public final class SignalContactRecord implements SignalRecord {
|
||||||
return proto.getHideStory();
|
return proto.getHideStory();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactRecord toProto() {
|
public ContactRecord toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ public final class SignalGroupV1Record implements SignalRecord {
|
||||||
return proto.getMutedUntilTimestamp();
|
return proto.getMutedUntilTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupV1Record toProto() {
|
public GroupV1Record toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ public final class SignalGroupV2Record implements SignalRecord {
|
||||||
return proto.getHideStory();
|
return proto.getHideStory();
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupV2Record toProto() {
|
public GroupV2Record toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
"Name" to "${Build.MODEL} (API ${Build.VERSION.SDK_INT})",
|
"Name" to "${Build.MODEL} (API ${Build.VERSION.SDK_INT})",
|
||||||
"Package" to packageName
|
"Package" to packageName
|
||||||
),
|
),
|
||||||
mapOf("main" to Spinner.DatabaseConfig(db = db))
|
mapOf("main" to Spinner.DatabaseConfig(db = db)),
|
||||||
|
emptyMap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,7 @@
|
||||||
<li {{#if isBrowse}}class="active"{{/if}}><a href="/browse?db={{database}}">Browse</a></li>
|
<li {{#if isBrowse}}class="active"{{/if}}><a href="/browse?db={{database}}">Browse</a></li>
|
||||||
<li {{#if isQuery}}class="active"{{/if}}><a href="/query?db={{database}}">Query</a></li>
|
<li {{#if isQuery}}class="active"{{/if}}><a href="/query?db={{database}}">Query</a></li>
|
||||||
<li {{#if isRecent}}class="active"{{/if}}><a href="/recent?db={{database}}">Recent</a></li>
|
<li {{#if isRecent}}class="active"{{/if}}><a href="/recent?db={{database}}">Recent</a></li>
|
||||||
|
{{#each plugins}}
|
||||||
|
<li {{#if (eq name activePlugin.name)}}class="active"{{/if}}><a href="{{path}}">{{name}}</a></li>
|
||||||
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<html>
|
||||||
|
{{> partials/head title=activePlugin.name }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{> partials/prefix}}
|
||||||
|
|
||||||
|
{{#if (eq "table" pluginResult.type)}}
|
||||||
|
<h1>Data</h1>
|
||||||
|
{{pluginResult.rowCount}} row(s). <br />
|
||||||
|
<br />
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
{{#each pluginResult.columns}}
|
||||||
|
<th>{{this}}</th>
|
||||||
|
{{/each}}
|
||||||
|
</tr>
|
||||||
|
{{#each pluginResult.rows}}
|
||||||
|
<tr>
|
||||||
|
{{#each this}}
|
||||||
|
<td><pre>{{{this}}}</pre></td>
|
||||||
|
{{/each}}
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
{{> partials/suffix }}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.signal.spinner
|
||||||
|
|
||||||
|
interface Plugin {
|
||||||
|
fun get(): PluginResult
|
||||||
|
val name: String
|
||||||
|
val path: String
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.signal.spinner
|
||||||
|
|
||||||
|
sealed class PluginResult(val type: String) {
|
||||||
|
data class TableResult(
|
||||||
|
val columns: List<String>,
|
||||||
|
val rows: List<List<String>>,
|
||||||
|
val rowCount: Int = rows.size,
|
||||||
|
) : PluginResult("table")
|
||||||
|
}
|
|
@ -18,9 +18,9 @@ object Spinner {
|
||||||
|
|
||||||
private lateinit var server: SpinnerServer
|
private lateinit var server: SpinnerServer
|
||||||
|
|
||||||
fun init(application: Application, deviceInfo: Map<String, String>, databases: Map<String, DatabaseConfig>) {
|
fun init(application: Application, deviceInfo: Map<String, String>, databases: Map<String, DatabaseConfig>, plugins: Map<String, Plugin>) {
|
||||||
try {
|
try {
|
||||||
server = SpinnerServer(application, deviceInfo, databases)
|
server = SpinnerServer(application, deviceInfo, databases, plugins)
|
||||||
server.start()
|
server.start()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.w(TAG, "Spinner server hit IO exception!", e)
|
Log.w(TAG, "Spinner server hit IO exception!", e)
|
||||||
|
|
|
@ -29,7 +29,8 @@ import kotlin.math.min
|
||||||
internal class SpinnerServer(
|
internal class SpinnerServer(
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
deviceInfo: Map<String, String>,
|
deviceInfo: Map<String, String>,
|
||||||
private val databases: Map<String, DatabaseConfig>
|
private val databases: Map<String, DatabaseConfig>,
|
||||||
|
private val plugins: Map<String, Plugin>
|
||||||
) : NanoHTTPD(5000) {
|
) : NanoHTTPD(5000) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -66,7 +67,14 @@ internal class SpinnerServer(
|
||||||
session.method == Method.GET && session.uri == "/query" -> getQuery(dbParam, dbConfig.db)
|
session.method == Method.GET && session.uri == "/query" -> getQuery(dbParam, dbConfig.db)
|
||||||
session.method == Method.POST && session.uri == "/query" -> postQuery(dbParam, dbConfig, session)
|
session.method == Method.POST && session.uri == "/query" -> postQuery(dbParam, dbConfig, session)
|
||||||
session.method == Method.GET && session.uri == "/recent" -> getRecent(dbParam, dbConfig.db)
|
session.method == Method.GET && session.uri == "/recent" -> getRecent(dbParam, dbConfig.db)
|
||||||
else -> newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_HTML, "Not found")
|
else -> {
|
||||||
|
val plugin = plugins[session.uri]
|
||||||
|
if (plugin != null && session.method == Method.GET) {
|
||||||
|
getPlugin(dbParam, plugin)
|
||||||
|
} else {
|
||||||
|
newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_HTML, "Not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Log.e(TAG, t)
|
Log.e(TAG, t)
|
||||||
|
@ -93,6 +101,7 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
tables = db.getTables().toTableInfo(),
|
tables = db.getTables().toTableInfo(),
|
||||||
indices = db.getIndexes().toIndexInfo(),
|
indices = db.getIndexes().toIndexInfo(),
|
||||||
triggers = db.getTriggers().toTriggerInfo(),
|
triggers = db.getTriggers().toTriggerInfo(),
|
||||||
|
@ -109,6 +118,7 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
tableNames = db.getTableNames()
|
tableNames = db.getTableNames()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -140,6 +150,7 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
tableNames = dbConfig.db.getTableNames(),
|
tableNames = dbConfig.db.getTableNames(),
|
||||||
table = table,
|
table = table,
|
||||||
queryResult = queryResult,
|
queryResult = queryResult,
|
||||||
|
@ -163,6 +174,7 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
query = ""
|
query = ""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -184,6 +196,7 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
recentSql = queries?.reversed()
|
recentSql = queries?.reversed()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -202,12 +215,28 @@ internal class SpinnerServer(
|
||||||
deviceInfo = deviceInfo,
|
deviceInfo = deviceInfo,
|
||||||
database = dbName,
|
database = dbName,
|
||||||
databases = databases.keys.toList(),
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
query = rawQuery,
|
query = rawQuery,
|
||||||
queryResult = dbConfig.db.query(query).toQueryResult(queryStartTime = startTime, columnTransformers = dbConfig.columnTransformers)
|
queryResult = dbConfig.db.query(query).toQueryResult(queryStartTime = startTime, columnTransformers = dbConfig.columnTransformers)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPlugin(dbName: String, plugin: Plugin): Response {
|
||||||
|
return renderTemplate(
|
||||||
|
"plugin",
|
||||||
|
PluginPageModel(
|
||||||
|
environment = environment,
|
||||||
|
deviceInfo = deviceInfo,
|
||||||
|
database = dbName,
|
||||||
|
databases = databases.keys.toList(),
|
||||||
|
plugins = plugins.values.toList(),
|
||||||
|
activePlugin = plugin,
|
||||||
|
pluginResult = plugin.get()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun internalError(throwable: Throwable): Response {
|
private fun internalError(throwable: Throwable): Response {
|
||||||
val stackTrace = ExceptionUtil.convertThrowableToString(throwable)
|
val stackTrace = ExceptionUtil.convertThrowableToString(throwable)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
|
@ -361,6 +390,7 @@ internal class SpinnerServer(
|
||||||
val deviceInfo: Map<String, String>
|
val deviceInfo: Map<String, String>
|
||||||
val database: String
|
val database: String
|
||||||
val databases: List<String>
|
val databases: List<String>
|
||||||
|
val plugins: List<Plugin>
|
||||||
}
|
}
|
||||||
|
|
||||||
data class OverviewPageModel(
|
data class OverviewPageModel(
|
||||||
|
@ -368,6 +398,7 @@ internal class SpinnerServer(
|
||||||
override val deviceInfo: Map<String, String>,
|
override val deviceInfo: Map<String, String>,
|
||||||
override val database: String,
|
override val database: String,
|
||||||
override val databases: List<String>,
|
override val databases: List<String>,
|
||||||
|
override val plugins: List<Plugin>,
|
||||||
val tables: List<TableInfo>,
|
val tables: List<TableInfo>,
|
||||||
val indices: List<IndexInfo>,
|
val indices: List<IndexInfo>,
|
||||||
val triggers: List<TriggerInfo>,
|
val triggers: List<TriggerInfo>,
|
||||||
|
@ -379,6 +410,7 @@ internal class SpinnerServer(
|
||||||
override val deviceInfo: Map<String, String>,
|
override val deviceInfo: Map<String, String>,
|
||||||
override val database: String,
|
override val database: String,
|
||||||
override val databases: List<String>,
|
override val databases: List<String>,
|
||||||
|
override val plugins: List<Plugin>,
|
||||||
val tableNames: List<String>,
|
val tableNames: List<String>,
|
||||||
val table: String? = null,
|
val table: String? = null,
|
||||||
val queryResult: QueryResult? = null,
|
val queryResult: QueryResult? = null,
|
||||||
|
@ -390,6 +422,7 @@ internal class SpinnerServer(
|
||||||
override val deviceInfo: Map<String, String>,
|
override val deviceInfo: Map<String, String>,
|
||||||
override val database: String,
|
override val database: String,
|
||||||
override val databases: List<String>,
|
override val databases: List<String>,
|
||||||
|
override val plugins: List<Plugin>,
|
||||||
val query: String = "",
|
val query: String = "",
|
||||||
val queryResult: QueryResult? = null
|
val queryResult: QueryResult? = null
|
||||||
) : PrefixPageData
|
) : PrefixPageData
|
||||||
|
@ -399,9 +432,20 @@ internal class SpinnerServer(
|
||||||
override val deviceInfo: Map<String, String>,
|
override val deviceInfo: Map<String, String>,
|
||||||
override val database: String,
|
override val database: String,
|
||||||
override val databases: List<String>,
|
override val databases: List<String>,
|
||||||
|
override val plugins: List<Plugin>,
|
||||||
val recentSql: List<RecentQuery>?
|
val recentSql: List<RecentQuery>?
|
||||||
) : PrefixPageData
|
) : PrefixPageData
|
||||||
|
|
||||||
|
data class PluginPageModel(
|
||||||
|
override val environment: String,
|
||||||
|
override val deviceInfo: Map<String, String>,
|
||||||
|
override val database: String,
|
||||||
|
override val databases: List<String>,
|
||||||
|
override val plugins: List<Plugin>,
|
||||||
|
val activePlugin: Plugin,
|
||||||
|
val pluginResult: PluginResult
|
||||||
|
) : PrefixPageData
|
||||||
|
|
||||||
data class QueryResult(
|
data class QueryResult(
|
||||||
val columns: List<String>,
|
val columns: List<String>,
|
||||||
val rows: List<List<String>>,
|
val rows: List<List<String>>,
|
||||||
|
|
Ładowanie…
Reference in New Issue