OTA Updates for adnroid app and web apk uploader

This commit is contained in:
2026-03-15 19:24:34 +08:00
parent 9bbaf67fef
commit 6fd3b66251
12 changed files with 1193 additions and 151 deletions
@@ -0,0 +1,32 @@
-- Create table that holds the latest version information for the
-- selfhosted Android update mechanism. Mobile clients query this table
-- anonymously and compare the returned `version_code`/`min_version_required`
-- against their own build number.
CREATE TABLE IF NOT EXISTS public.app_versions (
version_code text NOT NULL,
min_version_required text NOT NULL,
download_url text NOT NULL,
release_notes text DEFAULT ''
);
-- index for quick ordering by version_code
CREATE UNIQUE INDEX IF NOT EXISTS app_versions_version_code_idx
ON public.app_versions (version_code);
-- Enable realtime for clients if desired
ALTER PUBLICATION supabase_realtime ADD TABLE public.app_versions;
-- Row level security and simple policies
ALTER TABLE public.app_versions ENABLE ROW LEVEL SECURITY;
-- allow everyone (including anon) to read the table
DROP POLICY IF EXISTS "app_versions_public_read" ON public.app_versions;
CREATE POLICY "app_versions_public_read" ON public.app_versions
FOR SELECT USING (true);
-- only the service role (e.g. migrations or trusted server) may write
DROP POLICY IF EXISTS "app_versions_service_write" ON public.app_versions;
CREATE POLICY "app_versions_service_write" ON public.app_versions
FOR ALL USING (auth.role() = 'service_role')
WITH CHECK (auth.role() = 'service_role');
@@ -0,0 +1,48 @@
-- Ensure the buckets exist by inserting directly into the system table.
-- This matches the pattern used by older migrations and avoids relying on
-- optional SQL wrapper functions that may not be installed in all projects.
INSERT INTO storage.buckets (id, name, public)
VALUES ('apk_updates', 'apk_updates', true)
ON CONFLICT (id) DO NOTHING;
-- Ensure a `cors` column exists on storage.buckets for self-hosted installs
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'storage' AND table_name = 'buckets' AND column_name = 'cors'
) THEN
ALTER TABLE storage.buckets ADD COLUMN cors jsonb;
END IF;
END
$$;
-- Set bucket to public (idempotent) and populate a basic CORS rule for local development.
-- Adjust or remove origins as appropriate for production.
-- NOTE: bucket public/CORS is intentionally left to dashboard or manual configuration
-- to ensure compatibility with self-hosted Supabase instances. If you need to
-- programmatically set `public` or `cors` on your environment, run an update
-- in the SQL editor with values appropriate for your deployment.
-- storage object policies for the apk_updates bucket
-- Create policies in the same style as other buckets (e.g. it_service_attachments)
DROP POLICY IF EXISTS "Authenticated users can upload apk_updates" ON storage.objects;
CREATE POLICY "Authenticated users can upload apk_updates"
ON storage.objects FOR INSERT TO authenticated
WITH CHECK (bucket_id = 'apk_updates');
DROP POLICY IF EXISTS "Authenticated users can read apk_updates" ON storage.objects;
CREATE POLICY "Authenticated users can read apk_updates"
ON storage.objects FOR SELECT TO authenticated
USING (bucket_id = 'apk_updates');
DROP POLICY IF EXISTS "Authenticated users can delete apk_updates" ON storage.objects;
CREATE POLICY "Authenticated users can delete apk_updates"
ON storage.objects FOR DELETE TO authenticated
USING (bucket_id = 'apk_updates');
-- Note: some self-hosted Supabase installations may require manual CORS configuration
-- via the dashboard. This migration sets a CORS value in the buckets table when
-- possible and marks the bucket public; if uploads still fail with CORS errors,
-- update the bucket settings in the Supabase UI to add the correct origin(s).
@@ -0,0 +1,31 @@
-- Allow authenticated admin/dispatcher/it_staff profiles to write app_versions
-- while preserving service_role write access. This makes the web admin
-- uploader work for privileged users without exposing writes to all auth users.
-- Drop the restrictive service-only policy (if present)
DROP POLICY IF EXISTS "app_versions_service_write" ON public.app_versions;
-- Create a combined policy allowing either the service_role or a profile with
-- an elevated role to perform inserts/updates/deletes.
CREATE POLICY "app_versions_service_or_admin_write" ON public.app_versions
FOR ALL
USING (
auth.role() = 'service_role'
OR EXISTS (
SELECT 1 FROM public.profiles p
WHERE p.id = auth.uid() AND p.role IN ('admin', 'dispatcher', 'it_staff')
)
)
WITH CHECK (
auth.role() = 'service_role'
OR EXISTS (
SELECT 1 FROM public.profiles p
WHERE p.id = auth.uid() AND p.role IN ('admin', 'dispatcher', 'it_staff')
)
);
-- Notes:
-- - Run this migration using the service_role key (or apply via the Supabase
-- SQL editor) so the new policy is created successfully.
-- - If your project stores roles in a different table or column, adjust the
-- `SELECT` accordingly.
@@ -0,0 +1,12 @@
-- Grant appropriate privileges on app_versions so authenticated users
-- can perform writes permitted by RLS policies and anonymous users can read.
-- Grant read access to anonymous (public) clients
GRANT SELECT ON public.app_versions TO anon;
-- Grant write privileges to authenticated role (RLS still applies)
GRANT SELECT, INSERT, UPDATE, DELETE ON public.app_versions TO authenticated;
-- Notes:
-- - Run this with a service_role or a superuser in the SQL editor.
-- - RLS policies still control which authenticated users may actually write.
@@ -0,0 +1,11 @@
-- Ensure logical replication can publish deletes/updates for app_versions
-- by setting a replica identity. This is idempotent and safe to run
-- using the service_role key or from the Supabase SQL editor.
-- Set replica identity to FULL so deletes/updates include whole row
-- when no primary key is present.
ALTER TABLE public.app_versions REPLICA IDENTITY FULL;
-- Optional alternative: if you prefer a primary key instead of FULL,
-- run the following (only if version_code is guaranteed unique):
-- ALTER TABLE public.app_versions ADD CONSTRAINT app_versions_pkey PRIMARY KEY (version_code);
@@ -0,0 +1,32 @@
-- Migration: convert numeric version columns to text to support semantic versions
-- This is idempotent: it only alters columns if they are not already text.
DO $$
BEGIN
-- Alter version_code to text if needed
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'app_versions' AND column_name = 'version_code' AND data_type <> 'text'
) THEN
ALTER TABLE public.app_versions
ALTER COLUMN version_code TYPE text USING version_code::text;
END IF;
-- Alter min_version_required to text if needed
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'app_versions' AND column_name = 'min_version_required' AND data_type <> 'text'
) THEN
ALTER TABLE public.app_versions
ALTER COLUMN min_version_required TYPE text USING min_version_required::text;
END IF;
-- Recreate unique index on version_code to ensure correct index type
DROP INDEX IF EXISTS app_versions_version_code_idx;
CREATE UNIQUE INDEX IF NOT EXISTS app_versions_version_code_idx ON public.app_versions (version_code);
END
$$;
-- Notes:
-- - Run this migration using the service_role key or from the Supabase SQL editor.
-- - After running, clients using string semantic versions (e.g., '0.1.1') will work.