mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-19 05:08:20 +02:00
URL publique du site réglable depuis le backoffice
Connecter un réseau social imposait de poser NEXT_PUBLIC_SITE_URL dans docker-compose et de redémarrer le conteneur. L'écran Réseaux sociaux porte désormais le champ, pré-rempli avec l'adresse par laquelle l'administrateur consulte la page. - Nouveau réglage `site_public_url` ; `siteUrl()` lit la base puis retombe sur les variables d'environnement, donc les déploiements actuels ne bougent pas. - `siteUrl()`, `redirectUri()`, `authorizeUrl()`, `promoLink()` et `buildPromoMessage()` deviennent asynchrones. - Seule l'origine est conservée : le schéma est ajouté s'il manque, un chemin est retiré, et une adresse invalide revient sur l'écran avec un message au lieu d'une page d'erreur brute. - `saveSiteSettings` saute cette clé : le formulaire Paramètres ne la contient pas et l'aurait écrasée à chaque enregistrement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
9 files changed
+146
-37
No files matched your search
@@ -3,6 +3,9 @@ import { db } from "@/db";
|
||||
import { siteSettings } from "@/db/schema";
|
||||
|
||||
export const SITE_SETTING_DEFAULTS = {
|
||||
// URL publique de l'application : sert à l'adresse de retour OAuth des réseaux
|
||||
// sociaux et au lien inséré dans les publications.
|
||||
site_public_url: "",
|
||||
association_name: "Plein R",
|
||||
association_tagline: "Association des commerçants et entreprises du Bassin de Pompey.",
|
||||
association_intro:
|
||||
|
||||
+24
-10
@@ -1,6 +1,6 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "@/db";
|
||||
import { socialAccounts, type SocialAccount, type SocialNetwork } from "@/db/schema";
|
||||
import { siteSettings, socialAccounts, type SocialAccount, type SocialNetwork } from "@/db/schema";
|
||||
import { decryptSecret, encryptSecret } from "./crypto";
|
||||
|
||||
/**
|
||||
@@ -30,12 +30,21 @@ function env(key: string): string {
|
||||
return (process.env[key] ?? "").trim();
|
||||
}
|
||||
|
||||
export function siteUrl(): string {
|
||||
return (env("NEXT_PUBLIC_SITE_URL") || env("AUTH_URL")).replace(/\/+$/, "");
|
||||
/**
|
||||
* URL publique de l'application. Réglée dans Backend › Réseaux sociaux ; les
|
||||
* variables d'environnement restent acceptées en repli.
|
||||
*/
|
||||
export async function siteUrl(): Promise<string> {
|
||||
const [row] = await db
|
||||
.select({ value: siteSettings.value })
|
||||
.from(siteSettings)
|
||||
.where(eq(siteSettings.key, "site_public_url"));
|
||||
const stored = (row?.value ?? "").trim();
|
||||
return (stored || env("NEXT_PUBLIC_SITE_URL") || env("AUTH_URL")).replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
export function redirectUri(network: SocialNetwork): string {
|
||||
return `${siteUrl()}/api/social/${network}/callback`;
|
||||
export async function redirectUri(network: SocialNetwork): Promise<string> {
|
||||
return `${await siteUrl()}/api/social/${network}/callback`;
|
||||
}
|
||||
|
||||
// ---- Lecture ----
|
||||
@@ -123,11 +132,16 @@ const SCOPES: Record<SocialNetwork, string> = {
|
||||
linkedin: "w_organization_social r_organization_social rw_organization_admin",
|
||||
};
|
||||
|
||||
export function authorizeUrl(network: SocialNetwork, appId: string, state: string): string {
|
||||
export async function authorizeUrl(
|
||||
network: SocialNetwork,
|
||||
appId: string,
|
||||
state: string
|
||||
): Promise<string> {
|
||||
const redirect = await redirectUri(network);
|
||||
if (network === "facebook") {
|
||||
const params = new URLSearchParams({
|
||||
client_id: appId,
|
||||
redirect_uri: redirectUri("facebook"),
|
||||
redirect_uri: redirect,
|
||||
state,
|
||||
scope: SCOPES.facebook,
|
||||
response_type: "code",
|
||||
@@ -137,7 +151,7 @@ export function authorizeUrl(network: SocialNetwork, appId: string, state: strin
|
||||
const params = new URLSearchParams({
|
||||
response_type: "code",
|
||||
client_id: appId,
|
||||
redirect_uri: redirectUri("linkedin"),
|
||||
redirect_uri: redirect,
|
||||
state,
|
||||
scope: SCOPES.linkedin,
|
||||
});
|
||||
@@ -176,7 +190,7 @@ async function exchangeFacebook(
|
||||
`${base}/oauth/access_token?${new URLSearchParams({
|
||||
client_id: appId,
|
||||
client_secret: appSecret,
|
||||
redirect_uri: redirectUri("facebook"),
|
||||
redirect_uri: await redirectUri("facebook"),
|
||||
code,
|
||||
})}`
|
||||
);
|
||||
@@ -232,7 +246,7 @@ async function exchangeLinkedIn(
|
||||
code,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: redirectUri("linkedin"),
|
||||
redirect_uri: await redirectUri("linkedin"),
|
||||
}),
|
||||
});
|
||||
if (!tokenRes.ok) throw new SocialAuthError(`LinkedIn (code) : ${await readError(tokenRes)}`);
|
||||
|
||||
+6
-6
@@ -39,20 +39,20 @@ export type PromoForSharing = {
|
||||
memberName: string | null;
|
||||
};
|
||||
|
||||
export function promoLink(promo: PromoForSharing): string | null {
|
||||
const base = siteUrl();
|
||||
export async function promoLink(promo: PromoForSharing): Promise<string | null> {
|
||||
const base = await siteUrl();
|
||||
if (!base) return null;
|
||||
return promo.memberId ? `${base}/adherents/${promo.memberId}` : `${base}/#promotions`;
|
||||
}
|
||||
|
||||
/** Texte du post, commun aux deux réseaux. */
|
||||
export function buildPromoMessage(promo: PromoForSharing): string {
|
||||
export async function buildPromoMessage(promo: PromoForSharing): Promise<string> {
|
||||
const lines: string[] = [];
|
||||
lines.push(promo.badge ? `${promo.badge} — ${promo.title}` : promo.title);
|
||||
if (promo.memberName) lines.push(`Chez ${promo.memberName}`);
|
||||
if (promo.text?.trim()) lines.push("", promo.text.trim());
|
||||
if (promo.validUntil?.trim()) lines.push("", promo.validUntil.trim());
|
||||
const link = promoLink(promo);
|
||||
const link = await promoLink(promo);
|
||||
if (link) lines.push("", link);
|
||||
lines.push("", "#PleinR #BassinDePompey #CommerceLocal");
|
||||
return lines.join("\n");
|
||||
@@ -133,7 +133,7 @@ async function publishToFacebook(promo: PromoForSharing, message: string): Promi
|
||||
} else {
|
||||
endpoint = `${base}/feed`;
|
||||
body.set("message", message);
|
||||
const link = promoLink(promo);
|
||||
const link = await promoLink(promo);
|
||||
if (link) body.set("link", link);
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export async function publishPromoToNetwork(
|
||||
network: SocialNetwork,
|
||||
promo: PromoForSharing
|
||||
): Promise<PublishResult> {
|
||||
const message = buildPromoMessage(promo);
|
||||
const message = await buildPromoMessage(promo);
|
||||
return network === "facebook"
|
||||
? publishToFacebook(promo, message)
|
||||
: publishToLinkedIn(promo, message);
|
||||
|
||||
Reference in new issue
Block a user