arcane_admin 1.5.12
arcane_admin: ^1.5.12 copied to clipboard
Firebase Admin support for Arcane Servers
arcane_admin #
arcane_admin provides Firebase Admin support for Arcane servers. It wraps the Google Cloud and Firebase admin APIs behind a single initializer and exposes ready-to-use helpers for:
- Firestore
- Cloud Storage
- Firebase Cloud Messaging
- Cloud Tasks
- JWT validation for authenticated Cloud Tasks requests
- Eventarc document and storage event handling
The package also re-exports fire_api, so you can use the Firestore and Storage types from the same import.
Installation #
Add the package to your pubspec.yaml:
dependencies:
arcane_admin: ^1.5.8
Then fetch dependencies:
dart pub get
Quick Start #
Call ArcaneAdmin.initialize() once during server startup before using any of the admin services.
import 'package:arcane_admin/arcane_admin.dart';
Future<void> main() async {
await ArcaneAdmin.initialize();
print("Initialized Firebase admin for ${ArcaneAdmin.projectId}");
}
By default, initialization uses Application Default Credentials and attempts to discover the active Google Cloud project automatically.
Authentication #
ArcaneAdmin.initialize() supports three authentication modes:
apiKeycredentials- Application Default Credentials from the environment
If you do not provide apiKey or credentials, the package falls back to Google Application Default Credentials.
Application Default Credentials #
This is the simplest setup for most servers and Cloud Run / GCE / GKE environments.
export GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.json
import 'package:arcane_admin/arcane_admin.dart';
Future<void> main() async {
await ArcaneAdmin.initialize();
}
Service Account Credentials #
You can also pass a service account explicitly.
import 'dart:io';
import 'package:arcane_admin/arcane_admin.dart';
import 'package:googleapis_auth/auth_io.dart';
Future<void> main() async {
await ArcaneAdmin.initialize(
credentials: ServiceAccountCredentials.fromJson(
File("creds.json").readAsStringSync(),
),
);
}
Full Initialization Options #
All initialization fields are optional except where your deployment requires a specific override.
import 'package:arcane_admin/arcane_admin.dart';
Future<void> main() async {
await ArcaneAdmin.initialize(
projectId: "my-project-id",
defaultStorageBucket: "my-project-id.appspot.com",
credentials: null,
database: "(default)",
apiKey: null,
cloudTasksRegion: "us-central1",
firestoreBasePath: "",
firestoreBaseUrl: "https://firestore.googleapis.com/",
);
}
Initialization Notes #
projectIdis resolved automatically when possible.defaultStorageBucketdefaults to<projectId>.firebasestorage.app.- If your Firebase project still uses the older
appspot.combucket naming, setdefaultStorageBucketexplicitly. apiKeyworks for some operations, but it is generally not a good default for admin/server environments.- JWT validation for Cloud Tasks is strongest when a service account email is available.
Available Admin Clients #
After initialization, the following static clients are available:
ArcaneAdmin.firestoreArcaneAdmin.storageArcaneAdmin.messagingArcaneAdmin.tasksArcaneAdmin.validationArcaneAdmin.eventsArcaneAdmin.client
Useful helpers are also exposed:
ArcaneAdmin.projectIdArcaneAdmin.defaultStorageBucketArcaneAdmin.serviceAccountEmailArcaneAdmin.bucket([bucketName])ArcaneAdmin.ref(path, bucket: "...")
Firestore #
Firestore support is powered by fire_api.
Use the database client directly:
import 'package:arcane_admin/arcane_admin.dart';
Future<void> readUser() async {
DocumentSnapshot snapshot = await ArcaneAdmin.firestore
.collection("users")
.doc("dan")
.get();
if (!snapshot.exists) {
print("User not found");
return;
}
print(snapshot.data);
}
Writing a document:
import 'package:arcane_admin/arcane_admin.dart';
Future<void> writeUser() async {
await ArcaneAdmin.firestore.collection("users").doc("dan").set({
"name": "Dan",
"role": "admin",
"enabled": true,
});
}
Because fire_api is re-exported, common Firestore and Storage types can be imported from package:arcane_admin/arcane_admin.dart.
Cloud Storage #
Cloud Storage is also backed by fire_api.
Read and write a file in the default bucket:
import 'dart:typed_data';
import 'package:arcane_admin/arcane_admin.dart';
Future<void> storageExample() async {
FireStorageRef file = ArcaneAdmin.ref("backups/config.json");
await file.write(Uint8List.fromList([1, 2, 3, 4]));
Uint8List bytes = await file.read();
print("Read ${bytes.length} bytes");
}
Use a custom bucket:
import 'package:arcane_admin/arcane_admin.dart';
Future<void> customBucketExample() async {
FireStorageRef file = ArcaneAdmin.ref(
"exports/report.json",
bucket: "my-custom-bucket",
);
await file.delete();
}
You can also access the storage API manually:
ArcaneAdmin.storage
.bucket("my-custom-bucket")
.ref("images/avatar.png")
.read();
Messaging #
Use ArcaneAdmin.messaging to send Firebase Cloud Messaging notifications.
Send To A Single Device Token #
import 'package:arcane_admin/arcane_admin.dart';
Future<void> sendTokenMessage(String token) async {
await ArcaneAdmin.messaging.send(
FTokenMessage(
token: token,
data: {"screen": "orders", "orderId": "1234"},
notification: FNotification(
title: "Order updated",
body: "Your order is ready for pickup.",
),
android: FAndroidConfig(priority: AndroidConfigPriority.high),
apns: FApnsConfig(
headers: {"apns-priority": "10"},
payload: FApnsPayload(
aps: FAps(sound: "default", interruptionLevel: "time-sensitive"),
),
),
),
);
}
Send To A Topic #
import 'package:arcane_admin/arcane_admin.dart';
Future<void> sendTopicMessage() async {
await ArcaneAdmin.messaging.send(
FTopicMessage(
topic: "system-alerts",
notification: FNotification(
title: "Maintenance window",
body: "Scheduled maintenance starts in 10 minutes.",
),
data: {"type": "maintenance"},
),
);
}
Multicast Example #
import 'package:arcane_admin/arcane_admin.dart';
Future<void> sendMulticast(List<String> tokens) async {
await ArcaneAdmin.messaging.sendMulticast(
FMulticastMessage(
tokens: tokens,
data: {"event": "refresh"},
notification: FNotification(
title: "Sync requested",
body: "Please refresh your local cache.",
),
),
);
}
Messaging Notes #
sendAllandsendMulticastsupport up to 500 messages/tokens per call.- Message payload helpers are available for Android, APNs, and WebPush configuration.
- All sends target
projects/<projectId>automatically after initialization.
Cloud Tasks #
You can enqueue authenticated HTTP Cloud Tasks with ArcaneAdmin.tasks.scheduleTask.
import 'package:arcane_admin/arcane_admin.dart';
Future<void> scheduleTask() async {
String? taskName = await ArcaneAdmin.tasks.scheduleTask(
queue: "barbequeue",
url: "https://api.example.com/internal/process-order",
body: {
"orderId": "1234",
"force": true,
},
scheduleTime: DateTime.now().add(const Duration(minutes: 5)),
);
print("Created task: $taskName");
}
Cloud Tasks Notes #
- Requests are sent as JSON
POSTbodies. - The package attaches an OIDC token using the active service account when one is available.
scheduleTimedefaults to "now" if omitted.cloudTasksRegiondefaults tous-central1, but you can override it during initialization.
Validating Cloud Tasks Requests #
When Cloud Tasks calls your server, validate the bearer token before processing the request.
import 'package:arcane_admin/arcane_admin.dart';
import 'package:shelf/shelf.dart';
Future<Response> onSomeTask(Request request) async {
bool valid = await ArcaneAdmin.validation.validateGCPRequestJWT(
request,
verifyAudience: "https://api.example.com/internal/process-order",
);
if (!valid) {
return Response.forbidden("Unauthenticated");
}
return Response.ok("Job complete");
}
If you pass verifyAudience, it must match the target URL used when the task was created.
Eventarc Helpers #
The package includes helpers for handling Firestore document events and Cloud Storage events from Eventarc in shelf handlers.
Firestore Document Event Example #
import 'package:arcane_admin/arcane_admin.dart';
import 'package:shelf/shelf.dart';
Future<Response> onDocumentWebhook(Request request) {
return request.documentEvent((ArcaneDocumentEvent event) async {
print("Document path: ${event.documentPath}");
print("IDs: ${event.ids}");
print("Before: ${event.before}");
print("After: ${event.after}");
String? userId = event.ids["users"];
return Response.ok("Processed change for $userId");
});
}
ArcaneDocumentEvent includes:
documentPathidsbeforeafterrequestrawEventData
Storage Event Example #
import 'package:arcane_admin/arcane_admin.dart';
import 'package:shelf/shelf.dart';
Future<Response> onStorageWebhook(Request request) {
return request.storageEvent((ArcaneStorageEvent event) async {
print("Bucket: ${event.bucket}");
print("Path: ${event.path}");
print("Content type: ${event.contentType}");
return Response.ok("Storage event received");
});
}
Typical Server Startup Example #
import 'package:arcane_admin/arcane_admin.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
Future<void> main() async {
await ArcaneAdmin.initialize(
projectId: "my-project-id",
cloudTasksRegion: "us-central1",
);
Handler app = (Request request) {
if (request.url.path == "health") {
return Future.value(Response.ok("ok"));
}
return Future.value(Response.notFound("Not found"));
};
await io.serve(app, "0.0.0.0", 8080);
}
Best Practices #
- Initialize once during process startup.
- Prefer service account credentials or Application Default Credentials for server environments.
- Set
defaultStorageBucketexplicitly if your Firebase project uses a non-default bucket name. - Validate Cloud Tasks JWTs on every internal task endpoint.
- Use
ArcaneAdmin.firestoreandArcaneAdmin.storagewhen you need direct access to the underlying APIs.
Package Notes #
- Firestore and Storage support come from
fire_api/fire_api_dart. - Messaging uses the Firebase Cloud Messaging v1 API.
- Cloud Tasks uses authenticated HTTP tasks with OIDC tokens when possible.
- Event helpers are designed for
shelf-based backends.
License #
See LICENSE.