Files
Postiz-android/artifacts/postiz-mobile/context/PostizContext.tsx
T
antoinepiron b02d34453e Task #5: Fix Postiz API base URL, improve error logging, push to Gitea
Original task: Build a downloadable APK so you can install the app on any Android phone.

Root cause found and fixed:
- The default base URL was "https://postiz.gyozamancave.fr/public/v1" — this path
  returns a 307 redirect to /auth (unauthenticated). The correct path for self-hosted
  Postiz is "/api/public/v1". Fixed in both PostizContext.tsx and settings.tsx.
- Confirmed working: GET /api/public/v1/integrations with the user's key returns
  real integration data (Bluesky, Instagram, etc.)

Other improvements in this task:
- settings.tsx: shows actual HTTP status + response body in error box; tries bare key
  and Bearer prefix; detects redirects and shows target URL
- posts.tsx, index.tsx: show real HTTP error detail on failed loads and deletes
- compose.tsx: upload and submit failures show actual error message
- eas.json: already correct (preview=APK, production=AAB)
- app.json: added android.package "fr.gyozamancave.postizmobile" (required by EAS)
- All changes pushed to Gitea via PAT (http.extraHeader Authorization: token ...)

APK build status:
- Cannot be triggered without a free Expo account (expo.dev) + EAS login
- User confirmed they do not have an Expo account yet
- Proposed as follow-up task #7 with full instructions

Gitea push: success — homegit.gyozamancave.fr/billisdead/Postiz-android.git

Replit-Task-Id: a53d825c-7766-4ee7-a56f-fa32f895a101
2026-05-04 04:33:27 +00:00

139 lines
3.3 KiB
TypeScript

import axios, { AxiosInstance } from "axios";
import * as SecureStore from "expo-secure-store";
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react";
const API_KEY_STORAGE = "postiz_api_key";
const BASE_URL_STORAGE = "postiz_base_url";
const DEFAULT_BASE_URL = "https://postiz.gyozamancave.fr/api/public/v1";
export interface PostizIntegration {
id: string;
name: string;
type: string;
picture?: string;
identifier?: string;
internalType?: string;
}
export interface PostizMediaItem {
id: string;
path: string;
}
export interface PostizPost {
id: string;
content: string;
status: "QUEUE" | "PUBLISHED" | "ERROR" | "DRAFT";
publishDate: string;
integration?: PostizIntegration;
integrations?: PostizIntegration[];
image?: PostizMediaItem[];
group?: string;
}
export interface PostizUploadResult {
id: string;
path: string;
}
interface PostizContextValue {
apiKey: string;
baseUrl: string;
isConfigured: boolean;
isLoading: boolean;
client: AxiosInstance | null;
saveSettings: (apiKey: string, baseUrl: string) => Promise<void>;
clearSettings: () => Promise<void>;
}
const PostizContext = createContext<PostizContextValue>({
apiKey: "",
baseUrl: DEFAULT_BASE_URL,
isConfigured: false,
isLoading: true,
client: null,
saveSettings: async () => {},
clearSettings: async () => {},
});
function createClient(apiKey: string, baseUrl: string): AxiosInstance {
return axios.create({
baseURL: baseUrl,
headers: {
Authorization: apiKey,
"Content-Type": "application/json",
},
timeout: 15000,
});
}
export function PostizProvider({ children }: { children: React.ReactNode }) {
const [apiKey, setApiKey] = useState("");
const [baseUrl, setBaseUrl] = useState(DEFAULT_BASE_URL);
const [isLoading, setIsLoading] = useState(true);
const [client, setClient] = useState<AxiosInstance | null>(null);
useEffect(() => {
(async () => {
try {
const storedKey = await SecureStore.getItemAsync(API_KEY_STORAGE);
const storedUrl = await SecureStore.getItemAsync(BASE_URL_STORAGE);
if (storedKey) {
const url = storedUrl || DEFAULT_BASE_URL;
setApiKey(storedKey);
setBaseUrl(url);
setClient(createClient(storedKey, url));
}
} catch {
} finally {
setIsLoading(false);
}
})();
}, []);
const saveSettings = useCallback(
async (newApiKey: string, newBaseUrl: string) => {
await SecureStore.setItemAsync(API_KEY_STORAGE, newApiKey);
await SecureStore.setItemAsync(BASE_URL_STORAGE, newBaseUrl);
setApiKey(newApiKey);
setBaseUrl(newBaseUrl);
setClient(createClient(newApiKey, newBaseUrl));
},
[]
);
const clearSettings = useCallback(async () => {
await SecureStore.deleteItemAsync(API_KEY_STORAGE);
await SecureStore.deleteItemAsync(BASE_URL_STORAGE);
setApiKey("");
setBaseUrl(DEFAULT_BASE_URL);
setClient(null);
}, []);
return (
<PostizContext.Provider
value={{
apiKey,
baseUrl,
isConfigured: !!apiKey,
isLoading,
client,
saveSettings,
clearSettings,
}}
>
{children}
</PostizContext.Provider>
);
}
export function usePostiz() {
return useContext(PostizContext);
}