glanzlicht / MeinWeimar_APP

Official MeinWeimar App

Verified secure showcase
204 Files 2 MB Public size 14 Redacted Dart · PHP · XML · GITIGNORE · YAML · PLIST

Project overview

MeinWeimar_APP is a verified code showcase with 204 visible project entries across 2 top-level modules. The primary detected language is Dart; protected files remain visible in the structure while their contents stay private.

README

MeinWeimar — Flutter App & WordPress Plugin

> Working in Progress · Private portfolio repository · Active development

MeinWeimar is a location-based local platform for residents, students and visitors. This repository intentionally contains only two product parts:

  1. the Flutter consumer app;
  2. the meinweimar-core WordPress plugin that provides the editorial/admin
  3. layer and the REST contract consumed by the app.

The complete public website, WordPress core, databases, private infrastructure and unrelated platform modules are not part of this repository.

MeinWeimar consumer app preview

Why this project is interesting

MeinWeimar combines a mobile/web Flutter client with WordPress used as a structured content and administration backend. The goal is a maintainable product boundary: content, branding, navigation, categories, media and configuration are managed in WordPress and delivered through a versioned REST contract instead of being hardcoded in the Flutter UI.

The project demonstrates work across:

  • Flutter/Dart application architecture for iOS, Android and Web;
  • WordPress plugin architecture with custom post types, taxonomies, admin
  • settings and REST endpoints;

  • localized German/English UI and backend-controlled labels;
  • discovery/list/detail flows for places, services, events, classifieds, jobs
  • and real estate;

  • map, GPS, geocoding, marker and navigation contracts;
  • caching, ETag/config-version handling and offline-safe fallbacks;
  • role-aware account, claim, favorites, moderation and messaging boundaries;
  • automated Flutter tests, PHP/REST contract checks and release builds.

Repository structure

flutter-app/       Flutter consumer application
wordpress-plugin/  meinweimar-core WordPress plugin
screenshots/       Curated portfolio preview only

Current status

This is an active Working in Progress project. The core architecture and many end-to-end flows are implemented and continuously tested. Native device polish, production content, final legal/privacy review and further product modules are still being developed. The repository does not claim to be a finished production release.

Local development

Flutter app

cd flutter-app
flutter pub get
flutter analyze
flutter test
flutter run --dart-define=MW_API_BASE_URL=https://example.test/wp-json/meinweimar/v1

The API base URL and other environment-specific values are supplied at build or run time. No credentials are stored in the repository.

WordPress plugin

Copy wordpress-plugin into a WordPress installation's wp-content/plugins/meinweimar-core directory and activate it in the admin area. The plugin owns the MeinWeimar content types, editorial settings, permissions, REST responses, media/configuration contracts and frontend integration points.

Privacy and repository boundary

This repository is private and is prepared for controlled technical review. Secrets, production database exports, user data, WordPress uploads, private keys and infrastructure configuration must remain outside version control. The public-facing portfolio page uses a separate, redacted presentation layer and does not expose the private repository directly.

For technical reviewers

The most relevant entry points are:

  • flutter-app/lib/core — shared configuration, networking, localization,
  • theme and location contracts;

  • flutter-app/lib/features — consumer-facing product surfaces;
  • flutter-app/lib/repositories — API boundary and data access;
  • wordpress-plugin/includes — plugin services, REST, admin and contracts;
  • wordpress-plugin/assets — admin/frontend integration assets.

MeinWeimar is deliberately presented as an evolving product rather than a finished demo: the repository shows the architectural decisions, the working implementation and the remaining engineering work transparently.

wordpress-plugin/includes/class-messaging.phpPHP
<?php
declare(strict_types=1);
namespace MeinWeimar;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
final class Messaging
{
private const SCHEMA_VERSION = '0.1.0';
private const MAX_CIPHERTEXT_BYTES = 2_000_000;
private const MAX_HEADER_BYTES = 64_000;
public static function hooks(): void
{
add_action('rest_api_init', [self::class, 'register_routes']);
// Schema upgrades are write operations. Never run them as a side
// effect of a public page or REST request.
add_action('admin_init', [self::class, 'maybe_upgrade'], 1);
}
public static function table_name(): string
{
return self::table('messages');
}
public static function can_access_message(int $message_id, int $user_id): bool
{
global $wpdb;
return (bool) $wpdb->get_var($wpdb->prepare(
'SELECT m.id FROM ' . self::table('messages') . ' m INNER JOIN ' . self::table('message_participants') . ' p ON p.conversation_id = m.conversation_id WHERE m.id = %d AND p.user_id = %d AND p.left_at IS NULL LIMIT 1',
$message_id,
$user_id
));
}
public static function install(): void
{
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = $wpdb->get_charset_collate();
$devices = self::table('message_devices');
$conversations = self::table('message_conversations');
$participants = self::table('message_participants');
$messages = self::table('messages');
dbDelta("CREATE TABLE {$devices} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL,
device_id varchar(80) NOT NULL,
identity_key_b64 text NOT NULL,
signed_prekey_b64 text NOT NULL,
signed_prekey_signature_b64 text NOT NULL,
prekey_bundle_json longtext NOT NULL,
created_at datetime NOT NULL,
last_seen_at datetime NOT NULL,
revoked_at datetime NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY user_device (user_id,device_id),
KEY user_active (user_id,revoked_at)
) {$charset_collate};");
dbDelta("CREATE TABLE {$conversations} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
kind varchar(20) NOT NULL DEFAULT 'direct',
created_by bigint(20) unsigned NOT NULL,
status varchar(20) NOT NULL DEFAULT 'open',
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
KEY status_updated (status,updated_at),
KEY created_by (created_by)
) {$charset_collate};");
dbDelta("CREATE TABLE {$participants} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) unsigned NOT NULL,
user_id bigint(20) unsigned NOT NULL,
role varchar(20) NOT NULL DEFAULT 'member',
joined_at datetime NOT NULL,
left_at datetime NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY conversation_user (conversation_id,user_id),
KEY user_active (user_id,left_at)
) {$charset_collate};");
dbDelta("CREATE TABLE {$messages} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
conversation_id bigint(20) unsigned NOT NULL,
sender_user_id bigint(20) unsigned NOT NULL,
sender_device_id varchar(80) NOT NULL,
envelope_version varchar(32) NOT NULL,
crypto_suite varchar(64) NOT NULL,
ciphertext_b64 longtext NOT NULL,
header_b64 longtext NOT NULL,
client_message_id varchar(128) NOT NULL,
created_at datetime NOT NULL,
expires_at datetime NULL DEFAULT NULL,
PRIMARY KEY (id),
KEY conversation_id (conversation_id,id),
UNIQUE KEY message_dedupe (conversation_id,sender_user_id,client_message_id)
) {$charset_collate};");
update_option('meinweimar_messaging_schema_version', self::SCHEMA_VERSION, false);
}
public static function maybe_upgrade(): void
{
if (!current_user_can('manage_options')) {
return;
}
$schema_incomplete = self::schema_incomplete();
// A failed read is an uncertain database state. Do not turn it into
// an install/upgrade attempt; the next healthy admin request can
// retry without touching public traffic.
if (null === $schema_incomplete) {
return;
}
if (self::SCHEMA_VERSION !== (string) get_option('meinweimar_messaging_schema_version', '') || $schema_incomplete) {
self::install();
}
}
private static function schema_incomplete(): ?bool
{
global $wpdb;
$required_columns = [
self::table('message_devices') => [
'user_id', 'device_id', 'identity_key_b64', 'signed_prekey_b64',
'signed_prekey_signature_b64', 'prekey_bundle_json', 'revoked_at',
],
self::table('message_conversations') => [
'kind', 'created_by', 'status', 'created_at', 'updated_at',
],
self::table('message_participants') => [
'conversation_id', 'user_id', 'role', 'joined_at', 'left_at',
],
self::table('messages') => [
'conversation_id', 'sender_user_id', 'sender_device_id',
'envelope_version', 'crypto_suite', 'ciphertext_b64',
'header_b64', 'client_message_id', 'created_at', 'expires_at',
],
];
foreach ($required_columns as $table => $columns_required) {
$previous_error = (string) $wpdb->last_error;
$wpdb->last_error = '';
$exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table)));
$query_error = (string) $wpdb->last_error;
$wpdb->last_error = $previous_error;
if ($query_error !== '') {
return null;
}
if ($exists !== $table) {
return true;
}
$previous_error = (string) $wpdb->last_error;
$wpdb->last_error = '';
$columns = $wpdb->get_col("SHOW COLUMNS FROM {$table}", 0);
$query_error = (string) $wpdb->last_error;
$wpdb->last_error = $previous_error;
if ($query_error !== '' || !is_array($columns)) {
return null;
}
if ([] !== array_diff($columns_required, $columns)) {
return true;
}
}
return false;
}
public static function register_routes(): void
{
register_rest_route('meinweimar/v1', '/messaging/devices', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [self::class, 'list_own_devices'],
'permission_callback' => [self::class, 'require_login'],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [self::class, 'register_device'],
'permission_callback' => [self::class, 'require_login'],
],
]);
register_rest_route('meinweimar/v1', '/messaging/devices/(?P<device_id>[A-Za-z0-9._:-]+)', [
'methods' => WP_REST_Server::DELETABLE,
'callback' => [self::class, 'revoke_device'],
'permission_callback' => [self::class, 'require_login'],
]);
register_rest_route('meinweimar/v1', '/messaging/users/(?P<user_id>\d+)/devices', [
'methods' => WP_REST_Server::READABLE,
'callback' => [self::class, 'list_user_devices'],
'permission_callback' => [self::class, 'require_login'],
]);
register_rest_route('meinweimar/v1', '/messaging/conversations', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [self::class, 'list_conversations'],
'permission_callback' => [self::class, 'require_login'],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [self::class, 'create_conversation'],
'permission_callback' => [self::class, 'require_login'],
],
]);
register_rest_route('meinweimar/v1', '/messaging/conversations/(?P<id>\d+)/messages', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [self::class, 'list_messages'],
'permission_callback' => [self::class, 'require_login'],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [self::class, 'store_message'],
'permission_callback' => [self::class, 'require_login'],
],
]);
}
public static function require_login(): bool|WP_Error
{
return is_user_logged_in()
? true
: new WP_Error('meinweimar_auth_required', __('Anmeldung erforderlich.', 'meinweimar-core'), ['status' => 401]);
}
public static function list_own_devices(): WP_REST_Response
{
global $wpdb;
$rows = $wpdb->get_results($wpdb->prepare(
'SELECT device_id, identity_key_b64, signed_prekey_b64, signed_prekey_signature_b64, prekey_bundle_json, created_at, last_seen_at FROM ' . self::table('message_devices') . ' WHERE user_id = %d AND revoked_at IS NULL ORDER BY last_seen_at DESC',
get_current_user_id()
), ARRAY_A);
return new WP_REST_Response(['devices' => array_map([self::class, 'decode_device_row'], $rows)]);
}
public static function list_user_devices(WP_REST_Request $request): WP_REST_Response
{
global $wpdb;
$user_id = absint($request['user_id']);
if (Safety::is_blocked_between(get_current_user_id(), $user_id)) {
return new WP_REST_Response(['user_id' => $user_id, 'devices' => [], 'available' => false]);
}
$rows = $wpdb->get_results($wpdb->prepare(
'SELECT device_id, identity_key_b64, signed_prekey_b64, signed_prekey_signature_b64, prekey_bundle_json, created_at, last_seen_at FROM ' . self::table('message_devices') . ' WHERE user_id = %d AND revoked_at IS NULL ORDER BY last_seen_at DESC',
$user_id
), ARRAY_A);
return new WP_REST_Response(['user_id' => $user_id, 'devices' => array_map([self::class, 'decode_device_row'], $rows)]);
}
public static function register_device(WP_REST_Request $request): WP_REST_Response|WP_Error
{
global $wpdb;
$payload = self::payload($request);
$device_id = self::device_id($payload['device_id'] ?? '');
$identity_key = self::base64_field($payload['identity_key_b64'] ?? '', 4096);
$signed_prekey = self::base64_field($payload['signed_prekey_b64'] ?? '', 4096);
$signature = self::base64_field($payload['signed_prekey_signature_b64'] ?? '', 4096);
$bundle = $payload['prekey_bundle'] ?? null;
if (!$device_id || !$identity_key || !$signed_prekey || !$signature || !is_array($bundle) || self::contains_private_key_material($bundle)) {
return new WP_Error('meinweimar_invalid_device_bundle', __('Ein vollständiges öffentliches Geräte-/Prekey-Bundle ist erforderlich.', 'meinweimar-core'), ['status' => 400]);
}
$bundle_json = wp_json_encode($bundle, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if (!$bundle_json || strlen($bundle_json) > 100000) {
return new WP_Error('meinweimar_invalid_device_bundle', __('Das Geräte-Bundle ist ungültig oder zu groß.', 'meinweimar-core'), ['status' => 400]);
}
$now = current_time('mysql', true);
$sql = 'INSERT INTO ' . self::table('message_devices') . ' (user_id,device_id,identity_key_b64,signed_prekey_b64,signed_prekey_signature_b64,prekey_bundle_json,created_at,last_seen_at,revoked_at) VALUES (%d,%s,%s,%s,%s,%s,%s,%s,NULL) ON DUPLICATE KEY UPDATE identity_key_b64 = VALUES(identity_key_b64), signed_prekey_b64 = VALUES(signed_prekey_b64), signed_prekey_signature_b64 = VALUES(signed_prekey_signature_b64), prekey_bundle_json = VALUES(prekey_bundle_json), last_seen_at = VALUES(last_seen_at), revoked_at = NULL';
$result = $wpdb->query($wpdb->prepare($sql, get_current_user_id(), $device_id, $identity_key, $signed_prekey, $signature, $bundle_json, $now, $now));
if (false === $result) {
return new WP_Error('meinweimar_device_store_failed', __('Das Geräte-Bundle konnte nicht gespeichert werden.', 'meinweimar-core'), ['status' => 500]);
}
return new WP_REST_Response(['device_id' => $device_id, 'registered' => true], 201);
}
public static function revoke_device(WP_REST_Request $request): WP_REST_Response|WP_Error
{
global $wpdb;
$device_id = self::device_id($request['device_id'] ?? '');
if (!$device_id) {
return new WP_Error('meinweimar_invalid_device_id', __('Die Geräte-ID ist ungültig.', 'meinweimar-core'), ['status' => 400]);
}
$updated = $wpdb->query($wpdb->prepare(
'UPDATE ' . self::table('message_devices') . ' SET revoked_at = %s WHERE user_id = %d AND device_id = %s AND revoked_at IS NULL',
current_time('mysql', true),
get_current_user_id(),
$device_id
));
return new WP_REST_Response([
'device_id' => $device_id,
'revoked' => $updated > 0,
]);
}
public static function create_conversation(WP_REST_Request $request): WP_REST_Response|WP_Error
{
global $wpdb;
$payload = self::payload($request);
$participant_ids = array_map('absint', (array) ($payload['participant_user_ids'] ?? []));
$participant_ids[] = get_current_user_id();
$participant_ids = array_values(array_unique(array_filter($participant_ids)));
if (count($participant_ids) < 2 || count($participant_ids) > 50) {
return new WP_Error('meinweimar_invalid_participants', __('Eine Unterhaltung benötigt mindestens zwei und höchstens 50 Teilnehmer.', 'meinweimar-core'), ['status' => 400]);
}
foreach ($participant_ids as $user_id) {
if (!get_userdata($user_id)) {
return new WP_Error('meinweimar_unknown_participant', __('Mindestens ein Teilnehmer existiert nicht.', 'meinweimar-core'), ['status' => 400]);
}
if ($user_id !== get_current_user_id() && Safety::is_blocked_between(get_current_user_id(), $user_id)) {
return new WP_Error('meinweimar_participant_blocked', __('Mindestens ein Teilnehmer ist für diese Unterhaltung nicht verfügbar.', 'meinweimar-core'), ['status' => 403]);
}
}
$now = current_time('mysql', true);
$kind = count($participant_ids) === 2 ? 'direct' : 'group';
$wpdb->query('START TRANSACTION');
$inserted = $wpdb->insert(self::table('message_conversations'), [
'kind' => $kind,
'created_by' => get_current_user_id(),
'status' => 'open',
'created_at' => $now,
'updated_at' => $now,
], ['%s', '%d', '%s', '%s', '%s']);
if (!$inserted) {
$wpdb->query('ROLLBACK');
return new WP_Error('meinweimar_conversation_store_failed', __('Die Unterhaltung konnte nicht angelegt werden.', 'meinweimar-core'), ['status' => 500]);
}
$conversation_id = (int) $wpdb->insert_id;
foreach ($participant_ids as $user_id) {
$participant_inserted = $wpdb->insert(self::table('message_participants'), [
'conversation_id' => $conversation_id,
'user_id' => $user_id,
'role' => $user_id === get_current_user_id() ? 'owner' : 'member',
'joined_at' => $now,
], ['%d', '%d', '%s', '%s']);
if (!$participant_inserted) {
$wpdb->query('ROLLBACK');
return new WP_Error('meinweimar_participant_store_failed', __('Die Teilnehmer konnten nicht vollständig gespeichert werden.', 'meinweimar-core'), ['status' => 500]);
}
}
$wpdb->query('COMMIT');
return new WP_REST_Response(['conversation_id' => $conversation_id, 'kind' => $kind, 'participant_user_ids' => $participant_ids], 201);
}
public static function list_conversations(): WP_REST_Response
{
global $wpdb;
$rows = $wpdb->get_results($wpdb->prepare(
'SELECT c.id, c.kind, c.status, c.created_by, c.created_at, c.updated_at FROM ' . self::table('message_conversations') . ' c INNER JOIN ' . self::table('message_participants') . ' p ON p.conversation_id = c.id WHERE p.user_id = %d AND p.left_at IS NULL ORDER BY c.updated_at DESC LIMIT 100',
get_current_user_id()
), ARRAY_A);
return new WP_REST_Response(['conversations' => $rows]);
}
public static function list_messages(WP_REST_Request $request): WP_REST_Response|WP_Error
{
$conversation_id = absint($request['id']);
$access = self::check_membership($conversation_id);
if (is_wp_error($access)) {
return $access;
}
global $wpdb;
$limit = max(1, min(100, absint($request->get_param('limit') ?: 50)));
$before = max(0, absint($request->get_param('before')));
$where = 'conversation_id = %d';
$params = [$conversation_id];
if ($before) {
$where .= ' AND id < %d';
$params[] = $before;
}
$params[] = $limit;
$rows = $wpdb->get_results($wpdb->prepare(
'SELECT id, conversation_id, sender_user_id, sender_device_id, envelope_version, crypto_suite, ciphertext_b64, header_b64, client_message_id, created_at FROM ' . self::table('messages') . ' WHERE ' . $where . ' ORDER BY id DESC LIMIT %d',
...$params
), ARRAY_A);
return new WP_REST_Response(['messages' => array_reverse($rows), 'encrypted_only' => true]);
}
public static function store_message(WP_REST_Request $request): WP_REST_Response|WP_Error
{
global $wpdb;
$conversation_id = absint($request['id']);
$access = self::check_membership($conversation_id);
if (is_wp_error($access)) {
return $access;
}
$payload = self::payload($request);
$device_id = self::device_id($payload['sender_device_id'] ?? '');
$ciphertext = self::base64_field($payload['ciphertext_b64'] ?? '', self::MAX_CIPHERTEXT_BYTES);
$header = self::base64_field($payload['header_b64'] ?? '', self::MAX_HEADER_BYTES);
$client_message_id = self::opaque_text($payload['client_message_id'] ?? '', 128);
$crypto_suite = self::opaque_text($payload['crypto_suite'] ?? '', 64);
$envelope_version = self::opaque_text($payload['envelope_version'] ?? '', 32);
if (!$device_id || !$ciphertext || !$header || !$client_message_id || 'signal-protocol' !== $crypto_suite || 'mw-e2ee-envelope-v1' !== $envelope_version) {
return new WP_Error('meinweimar_invalid_envelope', __('Es wurde kein gültiger verschlüsselter Nachrichtenumschlag übergeben.', 'meinweimar-core'), ['status' => 400]);
}
if (!self::device_belongs_to_user($device_id, get_current_user_id())) {
return new WP_Error('meinweimar_unknown_sender_device', __('Das Absendergerät ist nicht für diesen Account registriert.', 'meinweimar-core'), ['status' => 403]);
}
$existing = $wpdb->get_row($wpdb->prepare(
'SELECT id, created_at FROM ' . self::table('messages') . ' WHERE conversation_id = %d AND sender_user_id = %d AND client_message_id = %s LIMIT 1',
$conversation_id,
get_current_user_id(),
$client_message_id
), ARRAY_A);
if ($existing) {
return new WP_REST_Response(['message_id' => (int) $existing['id'], 'client_message_id' => $client_message_id, 'stored' => true, 'replayed' => true], 200);
}
$now = current_time('mysql', true);
$inserted = $wpdb->insert(self::table('messages'), [
'conversation_id' => $conversation_id,
'sender_user_id' => get_current_user_id(),
'sender_device_id' => $device_id,
'envelope_version' => $envelope_version,
'crypto_suite' => $crypto_suite,
'ciphertext_b64' => $ciphertext,
'header_b64' => $header,
'client_message_id' => $client_message_id,
'created_at' => $now,
], ['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s']);
if (!$inserted) {
return new WP_Error('meinweimar_message_store_failed', __('Der verschlüsselte Nachrichtenumschlag konnte nicht gespeichert werden.', 'meinweimar-core'), ['status' => 500]);
}
$wpdb->update(self::table('message_conversations'), ['updated_at' => $now], ['id' => $conversation_id], ['%s'], ['%d']);
return new WP_REST_Response(['message_id' => (int) $wpdb->insert_id, 'client_message_id' => $client_message_id, 'stored' => true, 'replayed' => false], 201);
}
private static function check_membership(int $conversation_id): true|WP_Error
{
global $wpdb;
$exists = $wpdb->get_var($wpdb->prepare(
'SELECT p.id FROM ' . self::table('message_participants') . ' p INNER JOIN ' . self::table('message_conversations') . ' c ON c.id = p.conversation_id WHERE p.conversation_id = %d AND p.user_id = %d AND p.left_at IS NULL AND c.status = %s LIMIT 1',
$conversation_id,
get_current_user_id(),
'open'
));
if (!$exists) {
return new WP_Error('meinweimar_conversation_forbidden', __('Kein Zugriff auf diese Unterhaltung.', 'meinweimar-core'), ['status' => 403]);
}
$participant_ids = $wpdb->get_col($wpdb->prepare(
'SELECT user_id FROM ' . self::table('message_participants') . ' WHERE conversation_id = %d AND left_at IS NULL',
$conversation_id
));
foreach ((array) $participant_ids as $participant_id) {
if (Safety::is_blocked_between(get_current_user_id(), (int) $participant_id)) {
return new WP_Error('meinweimar_conversation_blocked', __('Der Zugriff auf diese Unterhaltung ist durch eine Blockierung eingeschränkt.', 'meinweimar-core'), ['status' => 403]);
}
}
return true;
}
private static function device_belongs_to_user(string $device_id, int $user_id): bool
{
global $wpdb;
return (bool) $wpdb->get_var($wpdb->prepare(
'SELECT id FROM ' . self::table('message_devices') . ' WHERE device_id = %s AND user_id = %d AND revoked_at IS NULL LIMIT 1',
$device_id,
$user_id
));
}
private static function decode_device_row(array $row): array
{
$bundle = json_decode((string) ($row['prekey_bundle_json'] ?? ''), true);
return [
'device_id' => (string) ($row['device_id'] ?? ''),
'identity_key_b64' => (string) ($row['identity_key_b64'] ?? ''),
'signed_prekey_b64' => (string) ($row['signed_prekey_b64'] ?? ''),
'signed_prekey_signature_b64' => (string) ($row['signed_prekey_signature_b64'] ?? ''),
'prekey_bundle' => is_array($bundle) ? $bundle : [],
'created_at' => (string) ($row['created_at'] ?? ''),
'last_seen_at' => (string) ($row['last_seen_at'] ?? ''),
];
}
private static function payload(WP_REST_Request $request): array
{
$payload = $request->get_json_params();
return is_array($payload) ? $payload : $request->get_params();
}
private static function opaque_text($value, int $max_length): string
{
if (!is_scalar($value)) {
return '';
}
$value = trim((string) $value);
if (strlen($value) > $max_length || preg_match('/[\x00-\x1F\x7F]/', $value)) {
return '';
}
return $value;
}
private static function device_id($value): string
{
$value = self::opaque_text($value, 80);
return $value && preg_match('/^[A-Za-z0-9._:-]+$/D', $value) ? $value : '';
}
private static function base64_field($value, int $max_decoded_bytes): string
{
$value = self::opaque_text($value, (int) ceil($max_decoded_bytes * 1.4) + 128);
if (!$value) {
return '';
}
$decoded = base64_decode($value, true);
return false !== $decoded && strlen($decoded) <= $max_decoded_bytes ? $value : '';
}
private static function contains_private_key_material(array $value): bool
{
foreach ($value as $key => $item) {
if (preg_match('/private|secret|seed|privatekey|secretkey/i', (string) $key)) {
return true;
}
if (is_array($item) && self::contains_private_key_material($item)) {
return true;
}
}
return false;
}
private static function table(string $suffix): string
{
global $wpdb;
return $wpdb->prefix . 'mw_' . $suffix;
}
}

Architecture

High-level modules detected from the verified public snapshot. Protected implementation details are intentionally omitted.

flutter-appmodule
wordpress-pluginmodule

Technology stack

DartPHPJavaScriptARBLOCKPBXPROJFlutter / Dart
Dart56.1%
PHP29.4%
JavaScript4.0%
ARB2.9%
LOCK1.7%
PBXPROJ1.7%
CSS1.5%
Markdown0.4%